Skip to content

Type inference of generics does not work only when callback takes argument #29123

Open
@ryym

Description

@ryym

I encountered a strange behavior of type inference about the generic arguments and intersection type. The error described below occurs only when the strictFunctionTypes option is enabled.


TypeScript Version:

Version 3.3.0-dev.20181221

Search Terms:

  • strictFunctionTypes
  • argument intersection type
  • generic argument
  • generic intersection type

Code

tsconfig.json:

{
  "compilerOptions": {
    "moduleResolution": "node",
    "target": "es6",
    "module":"commonjs",
    "strictFunctionTypes": true,
    "outDir": "dist"
  },
  "include": ["*.ts"]
}

source:

const run = <P1, P2>(
  makeProps1: (s: string) => P1,
  makeProps2: () => P2,
  useBoth: (props: P1 & P2) => void
) => {};

const foo = (props: { n: number; b: boolean }) => {};

// This works as expected.
run(() => ({ n: 1 }), () => ({ b: true }), foo);

// But if the first argument function takes a parameter `s`, it results in a compile error.
run(s => ({ n: 1 }), () => ({ b: true }), foo);

Expected behavior:

The type of run is inferred correctly from run(s => ({ n: 1 }), () => ({ b: true }), foo) as below and the code compiles.

const run: <{ n: number; }, { b: boolean; }>(makeProps1: (s: string) => { n: number; }, makeProps2: () => { b: boolean; }, useBoth: (props: { n: number; } & { b: boolean; }) => void) => void

This type is generated from run(() => ({ n: 1 }), () => ({ b: true }), foo). I copied it from Playground's tooltip.

Actual behavior:

It results in the compile error.

output of npx tsc:

repro.ts:9:43 - error TS2345: Argument of type '(props: { n: number; b: boolean; }) => void' is not assignable to parameter of type '(props: { b: boolean; }) => void'.                                  
  Types of parameters 'props' and 'props' are incompatible.
    Property 'n' is missing in type '{ b: boolean; }' but required in type '{ n: number; b: boolean; }'.                                                                                                 

9 run(s => ({ n: 1 }), () => ({ b: true }), foo);
                                            ~~~

  repro.ts:7:23
    7 const foo = (props: { n: number; b: boolean }) => {};
                            ~
    'n' is declared here.


Found 1 error.

This is because the return type of makeProps1 is inferred as {} instead of { n: number; }.

const run: <{}, { b: boolean; }>(makeProps1: (s: string) => {}, makeProps2: () => { b: boolean; }, useBoth: (props: { b: boolean; }) => void) => void

Playground Link:

Here. Note that this problem occurs only when the strictFunctionTypes option is enabled.

Related Issues:

I could not find.

Other Investigation:

In the code below, the type inference works as expected:

const run2 = <P1, P2>(
  makeProps1: (s: string) => P1,
  makeProps2: () => P2,
  useProps1: (p: P1) => void,
  useProps2: (p: P2) => void
) => {};

// The first argument function takes a parameter `s` but it does not change the type inference behavior.
run2(
  s => ({ n: 1 }),
  () => ({ b: true }),
  (p1: { n: number }) => {},
  (p2: { b: boolean }) => {}
);

So it seems that the error only occurs when the generic types (P1, P2) are used as intersection type.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScript

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions