Skip to content

Commit 0593ba2

Browse files
authored
Make getContextualTypeOfApparentType mapType over unions (#17668)
* Instantiate contextual types while in an inferrential context * Limit scope of instantiation to only when likely needed * Still get aparent type * Expand test * Fix nit * Handle JSX and array * Tests for the JSX and Array cases * After much deliberation and inspection, much simpler fix After much deliberation and inspection, much simpler fix Undo Redo
1 parent 4f48bf8 commit 0593ba2

6 files changed

+757
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13691,7 +13691,7 @@ namespace ts {
1369113691
// be "pushed" onto a node using the contextualType property.
1369213692
function getApparentTypeOfContextualType(node: Expression): Type {
1369313693
const type = getContextualType(node);
13694-
return type && getApparentType(type);
13694+
return type && mapType(type, getApparentType);
1369513695
}
1369613696

1369713697
/**
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
tests/cases/compiler/index.tsx(73,34): error TS7006: Parameter 's' implicitly has an 'any' type.
2+
3+
4+
==== tests/cases/compiler/index.tsx (1 errors) ====
5+
interface ActionsObject<State> {
6+
[prop: string]: (state: State) => State;
7+
}
8+
9+
interface Options<State, Actions> {
10+
state?: State;
11+
view?: (state: State, actions: Actions) => any;
12+
actions: string | Actions;
13+
}
14+
15+
declare function app<State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>): void;
16+
17+
app({
18+
state: 100,
19+
actions: {
20+
foo: s => s // Should be typed number => number
21+
},
22+
view: (s, a) => undefined as any,
23+
});
24+
25+
26+
interface Bar {
27+
bar: (a: number) => void;
28+
}
29+
30+
declare function foo<T extends Bar>(x: string | T): T;
31+
32+
const y = foo({
33+
bar(x) { // Should be typed number => void
34+
}
35+
});
36+
37+
interface Options2<State, Actions> {
38+
state?: State;
39+
view?: (state: State, actions: Actions) => any;
40+
actions?: Actions;
41+
}
42+
43+
declare function app2<State, Actions extends ActionsObject<State>>(obj: Options2<State, Actions>): void;
44+
45+
app2({
46+
state: 100,
47+
actions: {
48+
foo: s => s // Should be typed number => number
49+
},
50+
view: (s, a) => undefined as any,
51+
});
52+
53+
54+
type ActionsArray<State> = ((state: State) => State)[];
55+
56+
declare function app3<State, Actions extends ActionsArray<State>>(obj: Options<State, Actions>): void;
57+
58+
app3({
59+
state: 100,
60+
actions: [
61+
s => s // Should be typed number => number
62+
],
63+
view: (s, a) => undefined as any,
64+
});
65+
66+
namespace JSX {
67+
export interface Element {}
68+
export interface IntrinsicElements {}
69+
}
70+
71+
interface ActionsObjectOr<State> {
72+
[prop: string]: ((state: State) => State) | State;
73+
}
74+
75+
declare function App4<State, Actions extends ActionsObjectOr<State>>(props: Options<State, Actions>["actions"] & { state: State }): JSX.Element;
76+
77+
const a = <App4 state={100} foo={s => s} />; // TODO: should be number => number, but JSX resolution is missing an inferential pass
78+
~
79+
!!! error TS7006: Parameter 's' implicitly has an 'any' type.
80+
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//// [index.tsx]
2+
interface ActionsObject<State> {
3+
[prop: string]: (state: State) => State;
4+
}
5+
6+
interface Options<State, Actions> {
7+
state?: State;
8+
view?: (state: State, actions: Actions) => any;
9+
actions: string | Actions;
10+
}
11+
12+
declare function app<State, Actions extends ActionsObject<State>>(obj: Options<State, Actions>): void;
13+
14+
app({
15+
state: 100,
16+
actions: {
17+
foo: s => s // Should be typed number => number
18+
},
19+
view: (s, a) => undefined as any,
20+
});
21+
22+
23+
interface Bar {
24+
bar: (a: number) => void;
25+
}
26+
27+
declare function foo<T extends Bar>(x: string | T): T;
28+
29+
const y = foo({
30+
bar(x) { // Should be typed number => void
31+
}
32+
});
33+
34+
interface Options2<State, Actions> {
35+
state?: State;
36+
view?: (state: State, actions: Actions) => any;
37+
actions?: Actions;
38+
}
39+
40+
declare function app2<State, Actions extends ActionsObject<State>>(obj: Options2<State, Actions>): void;
41+
42+
app2({
43+
state: 100,
44+
actions: {
45+
foo: s => s // Should be typed number => number
46+
},
47+
view: (s, a) => undefined as any,
48+
});
49+
50+
51+
type ActionsArray<State> = ((state: State) => State)[];
52+
53+
declare function app3<State, Actions extends ActionsArray<State>>(obj: Options<State, Actions>): void;
54+
55+
app3({
56+
state: 100,
57+
actions: [
58+
s => s // Should be typed number => number
59+
],
60+
view: (s, a) => undefined as any,
61+
});
62+
63+
namespace JSX {
64+
export interface Element {}
65+
export interface IntrinsicElements {}
66+
}
67+
68+
interface ActionsObjectOr<State> {
69+
[prop: string]: ((state: State) => State) | State;
70+
}
71+
72+
declare function App4<State, Actions extends ActionsObjectOr<State>>(props: Options<State, Actions>["actions"] & { state: State }): JSX.Element;
73+
74+
const a = <App4 state={100} foo={s => s} />; // TODO: should be number => number, but JSX resolution is missing an inferential pass
75+
76+
77+
//// [index.jsx]
78+
app({
79+
state: 100,
80+
actions: {
81+
foo: function (s) { return s; } // Should be typed number => number
82+
},
83+
view: function (s, a) { return undefined; }
84+
});
85+
var y = foo({
86+
bar: function (x) {
87+
}
88+
});
89+
app2({
90+
state: 100,
91+
actions: {
92+
foo: function (s) { return s; } // Should be typed number => number
93+
},
94+
view: function (s, a) { return undefined; }
95+
});
96+
app3({
97+
state: 100,
98+
actions: [
99+
function (s) { return s; } // Should be typed number => number
100+
],
101+
view: function (s, a) { return undefined; }
102+
});
103+
var a = <App4 state={100} foo={function (s) { return s; }}/>; // TODO: should be number => number, but JSX resolution is missing an inferential pass

0 commit comments

Comments
 (0)