Skip to content

Commit 96894db

Browse files
ahejlsbergAndarist
andauthoredSep 30, 2022
Include type parameter defaults in contextual typing (#50994)
* Include type parameter defaults in contextual typing * Add tests * Add additional an test for instantiating contextual signature using default type param (#51002) * Update comment Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
1 parent 0d0a793 commit 96894db

12 files changed

+387
-3
lines changed
 

‎src/compiler/checker.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -27596,9 +27596,10 @@ namespace ts {
2759627596
function instantiateContextualType(contextualType: Type | undefined, node: Node, contextFlags: ContextFlags | undefined): Type | undefined {
2759727597
if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) {
2759827598
const inferenceContext = getInferenceContext(node);
27599-
// If no inferences have been made, nothing is gained from instantiating as type parameters
27600-
// would just be replaced with their defaults similar to the apparent type.
27601-
if (inferenceContext && contextFlags! & ContextFlags.Signature && some(inferenceContext.inferences, hasInferenceCandidates)) {
27599+
// If no inferences have been made, and none of the type parameters for which we are inferring
27600+
// specify default types, nothing is gained from instantiating as type parameters would just be
27601+
// replaced with their constraints similar to the apparent type.
27602+
if (inferenceContext && contextFlags! & ContextFlags.Signature && some(inferenceContext.inferences, hasInferenceCandidatesOrDefault)) {
2760227603
// For contextual signatures we incorporate all inferences made so far, e.g. from return
2760327604
// types as well as arguments to the left in a function call.
2760427605
return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper);
@@ -35209,6 +35210,10 @@ namespace ts {
3520935210
return !!(info.candidates || info.contraCandidates);
3521035211
}
3521135212

35213+
function hasInferenceCandidatesOrDefault(info: InferenceInfo) {
35214+
return !!(info.candidates || info.contraCandidates || hasTypeParameterDefault(info.typeParameter));
35215+
}
35216+
3521235217
function hasOverlappingInferences(a: InferenceInfo[], b: InferenceInfo[]) {
3521335218
for (let i = 0; i < a.length; i++) {
3521435219
if (hasInferenceCandidates(a[i]) && hasInferenceCandidates(b[i])) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
=== tests/cases/compiler/contextualSignatureConditionalTypeInstantiationUsingDefault.ts ===
2+
// repro #46310
3+
4+
export interface TypegenDisabled {
5+
>TypegenDisabled : Symbol(TypegenDisabled, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 0, 0))
6+
7+
"@@xstate/typegen": false;
8+
>"@@xstate/typegen" : Symbol(TypegenDisabled["@@xstate/typegen"], Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 2, 34))
9+
}
10+
export interface TypegenEnabled {
11+
>TypegenEnabled : Symbol(TypegenEnabled, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 4, 1))
12+
13+
"@@xstate/typegen": true;
14+
>"@@xstate/typegen" : Symbol(TypegenEnabled["@@xstate/typegen"], Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 5, 33))
15+
}
16+
17+
type ActionFunction<TEvent extends { type: string }> = (event: TEvent) => void;
18+
>ActionFunction : Symbol(ActionFunction, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 7, 1))
19+
>TEvent : Symbol(TEvent, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 9, 20))
20+
>type : Symbol(type, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 9, 36))
21+
>event : Symbol(event, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 9, 56))
22+
>TEvent : Symbol(TEvent, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 9, 20))
23+
24+
declare function createMachine<
25+
>createMachine : Symbol(createMachine, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 9, 79))
26+
27+
TTypesMeta extends TypegenEnabled | TypegenDisabled = TypegenDisabled
28+
>TTypesMeta : Symbol(TTypesMeta, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 11, 31))
29+
>TypegenEnabled : Symbol(TypegenEnabled, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 4, 1))
30+
>TypegenDisabled : Symbol(TypegenDisabled, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 0, 0))
31+
>TypegenDisabled : Symbol(TypegenDisabled, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 0, 0))
32+
33+
>(
34+
config: {
35+
>config : Symbol(config, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 13, 2))
36+
37+
types?: TTypesMeta;
38+
>types : Symbol(types, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 14, 11))
39+
>TTypesMeta : Symbol(TTypesMeta, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 11, 31))
40+
41+
},
42+
implementations: TTypesMeta extends TypegenEnabled
43+
>implementations : Symbol(implementations, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 16, 4))
44+
>TTypesMeta : Symbol(TTypesMeta, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 11, 31))
45+
>TypegenEnabled : Symbol(TypegenEnabled, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 4, 1))
46+
47+
? ActionFunction<{ type: "test" }>
48+
>ActionFunction : Symbol(ActionFunction, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 7, 1))
49+
>type : Symbol(type, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 18, 22))
50+
51+
: ActionFunction<{ type: string }>
52+
>ActionFunction : Symbol(ActionFunction, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 7, 1))
53+
>type : Symbol(type, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 19, 22))
54+
55+
): void;
56+
57+
createMachine({}, (ev) => {
58+
>createMachine : Symbol(createMachine, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 9, 79))
59+
>ev : Symbol(ev, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 22, 19))
60+
61+
ev.type; // should be `string`
62+
>ev.type : Symbol(type, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 19, 22))
63+
>ev : Symbol(ev, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 22, 19))
64+
>type : Symbol(type, Decl(contextualSignatureConditionalTypeInstantiationUsingDefault.ts, 19, 22))
65+
66+
});
67+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
=== tests/cases/compiler/contextualSignatureConditionalTypeInstantiationUsingDefault.ts ===
2+
// repro #46310
3+
4+
export interface TypegenDisabled {
5+
"@@xstate/typegen": false;
6+
>"@@xstate/typegen" : false
7+
>false : false
8+
}
9+
export interface TypegenEnabled {
10+
"@@xstate/typegen": true;
11+
>"@@xstate/typegen" : true
12+
>true : true
13+
}
14+
15+
type ActionFunction<TEvent extends { type: string }> = (event: TEvent) => void;
16+
>ActionFunction : ActionFunction<TEvent>
17+
>type : string
18+
>event : TEvent
19+
20+
declare function createMachine<
21+
>createMachine : <TTypesMeta extends TypegenDisabled | TypegenEnabled = TypegenDisabled>(config: { types?: TTypesMeta;}, implementations: TTypesMeta extends TypegenEnabled ? ActionFunction<{ type: "test";}> : ActionFunction<{ type: string;}>) => void
22+
23+
TTypesMeta extends TypegenEnabled | TypegenDisabled = TypegenDisabled
24+
>(
25+
config: {
26+
>config : { types?: TTypesMeta | undefined; }
27+
28+
types?: TTypesMeta;
29+
>types : TTypesMeta | undefined
30+
31+
},
32+
implementations: TTypesMeta extends TypegenEnabled
33+
>implementations : TTypesMeta extends TypegenEnabled ? ActionFunction<{ type: "test"; }> : ActionFunction<{ type: string; }>
34+
35+
? ActionFunction<{ type: "test" }>
36+
>type : "test"
37+
38+
: ActionFunction<{ type: string }>
39+
>type : string
40+
41+
): void;
42+
43+
createMachine({}, (ev) => {
44+
>createMachine({}, (ev) => { ev.type; // should be `string`}) : void
45+
>createMachine : <TTypesMeta extends TypegenDisabled | TypegenEnabled = TypegenDisabled>(config: { types?: TTypesMeta | undefined; }, implementations: TTypesMeta extends TypegenEnabled ? ActionFunction<{ type: "test"; }> : ActionFunction<{ type: string; }>) => void
46+
>{} : {}
47+
>(ev) => { ev.type; // should be `string`} : (ev: { type: string; }) => void
48+
>ev : { type: string; }
49+
50+
ev.type; // should be `string`
51+
>ev.type : string
52+
>ev : { type: string; }
53+
>type : string
54+
55+
});
56+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//// [genericInferenceDefaultTypeParameter.ts]
2+
// Repro from #50858
3+
4+
type Type = {
5+
a: (e: string) => void;
6+
b: (e: number) => void;
7+
}
8+
9+
declare function f1<T extends keyof Type = "a">(props: Type[T]): void;
10+
11+
f1(event => { });
12+
f1<"a">(event => { });
13+
f1<"b">(event => { });
14+
15+
16+
//// [genericInferenceDefaultTypeParameter.js]
17+
"use strict";
18+
// Repro from #50858
19+
f1(function (event) { });
20+
f1(function (event) { });
21+
f1(function (event) { });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/compiler/genericInferenceDefaultTypeParameter.ts ===
2+
// Repro from #50858
3+
4+
type Type = {
5+
>Type : Symbol(Type, Decl(genericInferenceDefaultTypeParameter.ts, 0, 0))
6+
7+
a: (e: string) => void;
8+
>a : Symbol(a, Decl(genericInferenceDefaultTypeParameter.ts, 2, 13))
9+
>e : Symbol(e, Decl(genericInferenceDefaultTypeParameter.ts, 3, 8))
10+
11+
b: (e: number) => void;
12+
>b : Symbol(b, Decl(genericInferenceDefaultTypeParameter.ts, 3, 27))
13+
>e : Symbol(e, Decl(genericInferenceDefaultTypeParameter.ts, 4, 8))
14+
}
15+
16+
declare function f1<T extends keyof Type = "a">(props: Type[T]): void;
17+
>f1 : Symbol(f1, Decl(genericInferenceDefaultTypeParameter.ts, 5, 1))
18+
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameter.ts, 7, 20))
19+
>Type : Symbol(Type, Decl(genericInferenceDefaultTypeParameter.ts, 0, 0))
20+
>props : Symbol(props, Decl(genericInferenceDefaultTypeParameter.ts, 7, 48))
21+
>Type : Symbol(Type, Decl(genericInferenceDefaultTypeParameter.ts, 0, 0))
22+
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameter.ts, 7, 20))
23+
24+
f1(event => { });
25+
>f1 : Symbol(f1, Decl(genericInferenceDefaultTypeParameter.ts, 5, 1))
26+
>event : Symbol(event, Decl(genericInferenceDefaultTypeParameter.ts, 9, 3))
27+
28+
f1<"a">(event => { });
29+
>f1 : Symbol(f1, Decl(genericInferenceDefaultTypeParameter.ts, 5, 1))
30+
>event : Symbol(event, Decl(genericInferenceDefaultTypeParameter.ts, 10, 8))
31+
32+
f1<"b">(event => { });
33+
>f1 : Symbol(f1, Decl(genericInferenceDefaultTypeParameter.ts, 5, 1))
34+
>event : Symbol(event, Decl(genericInferenceDefaultTypeParameter.ts, 11, 8))
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== tests/cases/compiler/genericInferenceDefaultTypeParameter.ts ===
2+
// Repro from #50858
3+
4+
type Type = {
5+
>Type : { a: (e: string) => void; b: (e: number) => void; }
6+
7+
a: (e: string) => void;
8+
>a : (e: string) => void
9+
>e : string
10+
11+
b: (e: number) => void;
12+
>b : (e: number) => void
13+
>e : number
14+
}
15+
16+
declare function f1<T extends keyof Type = "a">(props: Type[T]): void;
17+
>f1 : <T extends keyof Type = "a">(props: Type[T]) => void
18+
>props : Type[T]
19+
20+
f1(event => { });
21+
>f1(event => { }) : void
22+
>f1 : <T extends keyof Type = "a">(props: Type[T]) => void
23+
>event => { } : (event: string) => void
24+
>event : string
25+
26+
f1<"a">(event => { });
27+
>f1<"a">(event => { }) : void
28+
>f1 : <T extends keyof Type = "a">(props: Type[T]) => void
29+
>event => { } : (event: string) => void
30+
>event : string
31+
32+
f1<"b">(event => { });
33+
>f1<"b">(event => { }) : void
34+
>f1 : <T extends keyof Type = "a">(props: Type[T]) => void
35+
>event => { } : (event: number) => void
36+
>event : number
37+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//// [genericInferenceDefaultTypeParameterJsxReact.tsx]
2+
/// <reference path="/.lib/react16.d.ts" />
3+
4+
// Repro from #50858
5+
6+
import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react';
7+
8+
type ButtonBaseProps<T extends ElementType> = ComponentPropsWithRef<T> & { children?: ReactNode };
9+
10+
function Component<T extends ElementType = 'span'>(props: ButtonBaseProps<T>) {
11+
return <></>;
12+
}
13+
14+
const v1 = <Component onClick={e => e.preventDefault()} />;
15+
16+
17+
//// [genericInferenceDefaultTypeParameterJsxReact.js]
18+
"use strict";
19+
/// <reference path="react16.d.ts" />
20+
var __importDefault = (this && this.__importDefault) || function (mod) {
21+
return (mod && mod.__esModule) ? mod : { "default": mod };
22+
};
23+
exports.__esModule = true;
24+
// Repro from #50858
25+
var react_1 = __importDefault(require("react"));
26+
function Component(props) {
27+
return react_1["default"].createElement(react_1["default"].Fragment, null);
28+
}
29+
var v1 = react_1["default"].createElement(Component, { onClick: function (e) { return e.preventDefault(); } });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
=== tests/cases/compiler/genericInferenceDefaultTypeParameterJsxReact.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
4+
// Repro from #50858
5+
6+
import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react';
7+
>React : Symbol(React, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 6))
8+
>ComponentPropsWithRef : Symbol(ComponentPropsWithRef, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 15))
9+
>ElementType : Symbol(ElementType, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 38))
10+
>ReactNode : Symbol(ReactNode, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 51))
11+
12+
type ButtonBaseProps<T extends ElementType> = ComponentPropsWithRef<T> & { children?: ReactNode };
13+
>ButtonBaseProps : Symbol(ButtonBaseProps, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 77))
14+
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 21))
15+
>ElementType : Symbol(ElementType, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 38))
16+
>ComponentPropsWithRef : Symbol(ComponentPropsWithRef, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 15))
17+
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 21))
18+
>children : Symbol(children, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 74))
19+
>ReactNode : Symbol(ReactNode, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 51))
20+
21+
function Component<T extends ElementType = 'span'>(props: ButtonBaseProps<T>) {
22+
>Component : Symbol(Component, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 98))
23+
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 8, 19))
24+
>ElementType : Symbol(ElementType, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 38))
25+
>props : Symbol(props, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 8, 51))
26+
>ButtonBaseProps : Symbol(ButtonBaseProps, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 4, 77))
27+
>T : Symbol(T, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 8, 19))
28+
29+
return <></>;
30+
}
31+
32+
const v1 = <Component onClick={e => e.preventDefault()} />;
33+
>v1 : Symbol(v1, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 5))
34+
>Component : Symbol(Component, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 6, 98))
35+
>onClick : Symbol(onClick, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 21))
36+
>e : Symbol(e, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 31))
37+
>e.preventDefault : Symbol(React.SyntheticEvent.preventDefault, Decl(react16.d.ts, 642, 31))
38+
>e : Symbol(e, Decl(genericInferenceDefaultTypeParameterJsxReact.tsx, 12, 31))
39+
>preventDefault : Symbol(React.SyntheticEvent.preventDefault, Decl(react16.d.ts, 642, 31))
40+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/compiler/genericInferenceDefaultTypeParameterJsxReact.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
4+
// Repro from #50858
5+
6+
import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react';
7+
>React : typeof React
8+
>ComponentPropsWithRef : any
9+
>ElementType : any
10+
>ReactNode : any
11+
12+
type ButtonBaseProps<T extends ElementType> = ComponentPropsWithRef<T> & { children?: ReactNode };
13+
>ButtonBaseProps : ButtonBaseProps<T>
14+
>children : React.ReactNode
15+
16+
function Component<T extends ElementType = 'span'>(props: ButtonBaseProps<T>) {
17+
>Component : <T extends React.ElementType<any> = "span">(props: ButtonBaseProps<T>) => JSX.Element
18+
>props : ButtonBaseProps<T>
19+
20+
return <></>;
21+
><></> : JSX.Element
22+
}
23+
24+
const v1 = <Component onClick={e => e.preventDefault()} />;
25+
>v1 : JSX.Element
26+
><Component onClick={e => e.preventDefault()} /> : JSX.Element
27+
>Component : <T extends React.ElementType<any> = "span">(props: ButtonBaseProps<T>) => JSX.Element
28+
>onClick : (e: React.MouseEvent<HTMLSpanElement>) => void
29+
>e => e.preventDefault() : (e: React.MouseEvent<HTMLSpanElement>) => void
30+
>e : React.MouseEvent<HTMLSpanElement>
31+
>e.preventDefault() : void
32+
>e.preventDefault : () => void
33+
>e : React.MouseEvent<HTMLSpanElement>
34+
>preventDefault : () => void
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// repro #46310
5+
6+
export interface TypegenDisabled {
7+
"@@xstate/typegen": false;
8+
}
9+
export interface TypegenEnabled {
10+
"@@xstate/typegen": true;
11+
}
12+
13+
type ActionFunction<TEvent extends { type: string }> = (event: TEvent) => void;
14+
15+
declare function createMachine<
16+
TTypesMeta extends TypegenEnabled | TypegenDisabled = TypegenDisabled
17+
>(
18+
config: {
19+
types?: TTypesMeta;
20+
},
21+
implementations: TTypesMeta extends TypegenEnabled
22+
? ActionFunction<{ type: "test" }>
23+
: ActionFunction<{ type: string }>
24+
): void;
25+
26+
createMachine({}, (ev) => {
27+
ev.type; // should be `string`
28+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// @strict: true
2+
3+
// Repro from #50858
4+
5+
type Type = {
6+
a: (e: string) => void;
7+
b: (e: number) => void;
8+
}
9+
10+
declare function f1<T extends keyof Type = "a">(props: Type[T]): void;
11+
12+
f1(event => { });
13+
f1<"a">(event => { });
14+
f1<"b">(event => { });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// @strict: true
2+
// @esModuleInterop: true
3+
// @jsx: react
4+
5+
/// <reference path="/.lib/react16.d.ts" />
6+
7+
// Repro from #50858
8+
9+
import React, { ComponentPropsWithRef, ElementType, ReactNode } from 'react';
10+
11+
type ButtonBaseProps<T extends ElementType> = ComponentPropsWithRef<T> & { children?: ReactNode };
12+
13+
function Component<T extends ElementType = 'span'>(props: ButtonBaseProps<T>) {
14+
return <></>;
15+
}
16+
17+
const v1 = <Component onClick={e => e.preventDefault()} />;

0 commit comments

Comments
 (0)
Please sign in to comment.