microsoft / microsoft/TypeScript
Type inference of generics does not work only when callback takes argument
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 132
Description
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:
strictFunctionTypesargument intersection typegeneric argumentgeneric 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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the repro.ts example and tsconfig.json, then run npx tsc with strictFunctionTypes enabled to confirm the inference failure. Trace how the generic return types are inferred when P1 and P2 are used as an intersection, and verify completion when the callback takes an argument without changing the expected inferred types.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100