Skip to content

Generic function argument lost when passed to a generic identity function #38007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
invliD opened this issue Apr 16, 2020 · 1 comment
Closed
Assignees
Labels
Duplicate An existing issue was already created

Comments

@invliD
Copy link

invliD commented Apr 16, 2020

TypeScript Version: 3.8.3

Search Terms:

  • generic function
  • "instantiated with a different subtype of constraint"

Expected behavior:
In TypeScript 3.7.5, the function returned by define is of type <E>(argument: E, somethingElse: { key: E; }) => (a: string) => number.

Actual behavior:
In TypeScript 3.8.3, the function returned by define is of type (argument: E, somethingElse: { key: E; }) => (a: string) => number.

I believe this behavior is a regression, since a function with generic argument types without itself being generic doesn't really make sense, and the coded did work with TS 3.7. My assumption here is that TS doesn't understand the two Es actually must be the same type.

Code

// In reality there are more arguments of an Action and they have complex types
type Action<R> = (a: string) => R;

// The purpose of this method is to allow definitions of action creators without explicitly typing an
// Action's argument types every time
function define<C extends (...args: any[]) => Action<any>>(creator: C): C {
    return creator;
}

// Type inferred in TS 3.7.5: <E>(argument: E, somethingElse: { key: E; }) => (a: string) => number
// Type inferred in TS 3.8.3: (argument: E, somethingElse: { key: E; }) => (a: string) => number
const definedAction = define(
    <E>(
        argument: E,
        somethingElse: { key: E }
    ) => (a /* a has a properly inferred type */) => 5,
);

const extra = { test: "" };

definedAction(extra, { key: extra });
/*
Error only in TS 3.8.3:
Argument of type '{ test: string; }' is not assignable to parameter of type 'E'.
  '{ test: string; }' is assignable to the constraint of type 'E', but 'E' could be instantiated with a different subtype of constraint '{}'.(2345)
*/
Output
"use strict";
// The purpose of this method is to allow definitions of action creators without explicitly typing an
// Action's argument types every time
function define(creator) {
    return creator;
}
// Type inferred in TS 3.7.5: <E>(argument: E, somethingElse: { key: E; }) => (a: string) => number
// Type inferred in TS 3.8.3: (argument: E, somethingElse: { key: E; }) => (a: string) => number
const definedAction = define((argument, somethingElse) => (a /* a has a properly inferred type */) => 5);
const extra = { test: "" };
definedAction(extra, { key: extra });
/*
Error only in TS 3.8.3:
Argument of type '{ test: string; }' is not assignable to parameter of type 'E'.
  '{ test: string; }' is assignable to the constraint of type 'E', but 'E' could be instantiated with a different subtype of constraint '{}'.(2345)
*/
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "useDefineForClassFields": false,
    "alwaysStrict": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "downlevelIteration": false,
    "noEmitHelpers": false,
    "noLib": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "esModuleInterop": true,
    "preserveConstEnums": false,
    "removeComments": false,
    "skipLibCheck": false,
    "checkJs": false,
    "allowJs": false,
    "declaration": true,
    "experimentalDecorators": false,
    "emitDecoratorMetadata": false,
    "target": "ES2017",
    "module": "ESNext"
  }
}

Playground Link: Provided

Related issues:

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label Apr 17, 2020
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 4.0 milestone Apr 17, 2020
@ahejlsberg
Copy link
Member

Duplicate of #37110. Fixed in #37811. No longer repros in nightly build.

@ahejlsberg ahejlsberg added Duplicate An existing issue was already created and removed Needs Investigation This issue needs a team member to investigate its status. labels Apr 17, 2020
@ahejlsberg ahejlsberg removed this from the TypeScript 4.0 milestone Apr 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants