Skip to content

Commit c938834

Browse files
committed
Infer types from mapped properties other than the mapped type itself
1 parent ed15865 commit c938834

12 files changed

+128
-45
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24583,6 +24583,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2458324583
if (getObjectFlags(target) & ObjectFlags.Mapped && !(target as MappedType).declaration.nameType) {
2458424584
const constraintType = getConstraintTypeFromMappedType(target as MappedType);
2458524585
if (inferToMappedType(source, target as MappedType, constraintType)) {
24586+
inferFromMappedProperties(source, target as MappedType);
2458624587
return;
2458724588
}
2458824589
}
@@ -24699,6 +24700,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2469924700
}
2470024701
}
2470124702

24703+
function inferFromMappedProperties(source: Type, target: MappedType) {
24704+
const properties = getPropertiesOfObjectType(source);
24705+
for (const sourceProp of properties) {
24706+
if (!some(sourceProp.declarations, hasSkipDirectInferenceFlag)) {
24707+
const targetProp = substituteIndexedMappedType(target, getStringLiteralType(unescapeLeadingUnderscores(sourceProp.escapedName)));
24708+
inferFromTypes(getTypeOfSymbol(sourceProp), targetProp);
24709+
}
24710+
}
24711+
}
24712+
2470224713
function inferFromSignatures(source: Type, target: Type, kind: SignatureKind) {
2470324714
const sourceSignatures = getSignaturesOfType(source, kind);
2470424715
const targetSignatures = getSignaturesOfType(target, kind);

tests/baselines/reference/mappedTypeRecursiveInference.errors.txt

Lines changed: 3 additions & 9 deletions
Large diffs are not rendered by default.

tests/baselines/reference/mappedTypeRecursiveInference.symbols

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,8 @@ const out2 = foo(xhr);
9393
>xhr : Symbol(xhr, Decl(mappedTypeRecursiveInference.ts, 17, 3))
9494

9595
out2.responseXML
96-
>out2.responseXML : Symbol(responseXML, Decl(lib.dom.d.ts, --, --))
9796
>out2 : Symbol(out2, Decl(mappedTypeRecursiveInference.ts, 18, 5))
98-
>responseXML : Symbol(responseXML, Decl(lib.dom.d.ts, --, --))
9997

10098
out2.responseXML.activeElement.className.length
101-
>out2.responseXML.activeElement.className.length : Symbol(length, Decl(lib.es5.d.ts, --, --))
102-
>out2.responseXML.activeElement.className : Symbol(className, Decl(lib.dom.d.ts, --, --))
103-
>out2.responseXML.activeElement : Symbol(activeElement, Decl(lib.dom.d.ts, --, --))
104-
>out2.responseXML : Symbol(responseXML, Decl(lib.dom.d.ts, --, --))
10599
>out2 : Symbol(out2, Decl(mappedTypeRecursiveInference.ts, 18, 5))
106-
>responseXML : Symbol(responseXML, Decl(lib.dom.d.ts, --, --))
107-
>activeElement : Symbol(activeElement, Decl(lib.dom.d.ts, --, --))
108-
>className : Symbol(className, Decl(lib.dom.d.ts, --, --))
109-
>length : Symbol(length, Decl(lib.es5.d.ts, --, --))
110100

tests/baselines/reference/mappedTypeRecursiveInference.types

Lines changed: 14 additions & 14 deletions
Large diffs are not rendered by default.

tests/baselines/reference/reverseMappedTypeContextualTypeNotCircular.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/compiler/reverseMappedTypeContextualTypeNotCircular.ts(10,3): error TS2322: Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'.
1+
tests/cases/compiler/reverseMappedTypeContextualTypeNotCircular.ts(10,3): error TS2322: Type '(state: any, props: any) => {}' is not assignable to type 'Selector<any, {}>'.
22

33

44
==== tests/cases/compiler/reverseMappedTypeContextualTypeNotCircular.ts (1 errors) ====
@@ -11,8 +11,8 @@ tests/cases/compiler/reverseMappedTypeContextualTypeNotCircular.ts(10,3): error
1111
const editable = () => ({});
1212

1313
const mapStateToProps = createStructuredSelector({
14-
editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error
14+
editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<any, {}>'", _not_ a circularity error
1515
~~~~~~~~
16-
!!! error TS2322: Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'.
17-
!!! related TS6500 tests/cases/compiler/reverseMappedTypeContextualTypeNotCircular.ts:10:3: The expected type comes from property 'editable' which is declared here on type '{ editable: Selector<unknown, {}>; }'
16+
!!! error TS2322: Type '(state: any, props: any) => {}' is not assignable to type 'Selector<any, {}>'.
17+
!!! related TS6500 tests/cases/compiler/reverseMappedTypeContextualTypeNotCircular.ts:10:3: The expected type comes from property 'editable' which is declared here on type '{ editable: Selector<any, {}>; }'
1818
});

tests/baselines/reference/reverseMappedTypeContextualTypeNotCircular.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ declare function createStructuredSelector<S, T>(
88
const editable = () => ({});
99

1010
const mapStateToProps = createStructuredSelector({
11-
editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error
11+
editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<any, {}>'", _not_ a circularity error
1212
});
1313

1414
//// [reverseMappedTypeContextualTypeNotCircular.js]
1515
var editable = function () { return ({}); };
1616
var mapStateToProps = createStructuredSelector({
17-
editable: function (state, props) { return editable(); }, // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error
17+
editable: function (state, props) { return editable(); }, // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<any, {}>'", _not_ a circularity error
1818
});

tests/baselines/reference/reverseMappedTypeContextualTypeNotCircular.symbols

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const mapStateToProps = createStructuredSelector({
3333
>mapStateToProps : Symbol(mapStateToProps, Decl(reverseMappedTypeContextualTypeNotCircular.ts, 8, 5))
3434
>createStructuredSelector : Symbol(createStructuredSelector, Decl(reverseMappedTypeContextualTypeNotCircular.ts, 0, 38))
3535

36-
editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error
36+
editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<any, {}>'", _not_ a circularity error
3737
>editable : Symbol(editable, Decl(reverseMappedTypeContextualTypeNotCircular.ts, 8, 50))
3838
>state : Symbol(state, Decl(reverseMappedTypeContextualTypeNotCircular.ts, 9, 13))
3939
>props : Symbol(props, Decl(reverseMappedTypeContextualTypeNotCircular.ts, 9, 24))

tests/baselines/reference/reverseMappedTypeContextualTypeNotCircular.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ const editable = () => ({});
1818
>{} : {}
1919

2020
const mapStateToProps = createStructuredSelector({
21-
>mapStateToProps : Selector<unknown, { editable: {}; }>
22-
>createStructuredSelector({ editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error}) : Selector<unknown, { editable: {}; }>
21+
>mapStateToProps : Selector<any, { editable: {}; }>
22+
>createStructuredSelector({ editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<any, {}>'", _not_ a circularity error}) : Selector<any, { editable: {}; }>
2323
>createStructuredSelector : <S, T>(selectors: { [K in keyof T]: Selector<S, T[K]>; }) => Selector<S, T>
24-
>{ editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error} : { editable: (state: any, props: any) => {}; }
24+
>{ editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<any, {}>'", _not_ a circularity error} : { editable: (state: any, props: any) => {}; }
2525

26-
editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error
26+
editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<any, {}>'", _not_ a circularity error
2727
>editable : (state: any, props: any) => {}
2828
>(state: any, props: any) => editable() : (state: any, props: any) => {}
2929
>state : any
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/compiler/reverseMappedTypeInferFromItsProperties.ts ===
2+
// repro from #29479
3+
4+
type Selector<S, R> = (state: S) => R;
5+
>Selector : Symbol(Selector, Decl(reverseMappedTypeInferFromItsProperties.ts, 0, 0))
6+
>S : Symbol(S, Decl(reverseMappedTypeInferFromItsProperties.ts, 2, 14))
7+
>R : Symbol(R, Decl(reverseMappedTypeInferFromItsProperties.ts, 2, 16))
8+
>state : Symbol(state, Decl(reverseMappedTypeInferFromItsProperties.ts, 2, 23))
9+
>S : Symbol(S, Decl(reverseMappedTypeInferFromItsProperties.ts, 2, 14))
10+
>R : Symbol(R, Decl(reverseMappedTypeInferFromItsProperties.ts, 2, 16))
11+
12+
declare function createStructuredSelector<S, T>(selectors: {
13+
>createStructuredSelector : Symbol(createStructuredSelector, Decl(reverseMappedTypeInferFromItsProperties.ts, 2, 38))
14+
>S : Symbol(S, Decl(reverseMappedTypeInferFromItsProperties.ts, 4, 42))
15+
>T : Symbol(T, Decl(reverseMappedTypeInferFromItsProperties.ts, 4, 44))
16+
>selectors : Symbol(selectors, Decl(reverseMappedTypeInferFromItsProperties.ts, 4, 48))
17+
18+
[K in keyof T]: Selector<S, T[K]>;
19+
>K : Symbol(K, Decl(reverseMappedTypeInferFromItsProperties.ts, 5, 3))
20+
>T : Symbol(T, Decl(reverseMappedTypeInferFromItsProperties.ts, 4, 44))
21+
>Selector : Symbol(Selector, Decl(reverseMappedTypeInferFromItsProperties.ts, 0, 0))
22+
>S : Symbol(S, Decl(reverseMappedTypeInferFromItsProperties.ts, 4, 42))
23+
>T : Symbol(T, Decl(reverseMappedTypeInferFromItsProperties.ts, 4, 44))
24+
>K : Symbol(K, Decl(reverseMappedTypeInferFromItsProperties.ts, 5, 3))
25+
26+
}): Selector<S, T>;
27+
>Selector : Symbol(Selector, Decl(reverseMappedTypeInferFromItsProperties.ts, 0, 0))
28+
>S : Symbol(S, Decl(reverseMappedTypeInferFromItsProperties.ts, 4, 42))
29+
>T : Symbol(T, Decl(reverseMappedTypeInferFromItsProperties.ts, 4, 44))
30+
31+
type State = { foo: number };
32+
>State : Symbol(State, Decl(reverseMappedTypeInferFromItsProperties.ts, 6, 19))
33+
>foo : Symbol(foo, Decl(reverseMappedTypeInferFromItsProperties.ts, 8, 14))
34+
35+
declare const mySelector: Selector<State, boolean>;
36+
>mySelector : Symbol(mySelector, Decl(reverseMappedTypeInferFromItsProperties.ts, 10, 13))
37+
>Selector : Symbol(Selector, Decl(reverseMappedTypeInferFromItsProperties.ts, 0, 0))
38+
>State : Symbol(State, Decl(reverseMappedTypeInferFromItsProperties.ts, 6, 19))
39+
40+
export const result = createStructuredSelector({ mySelector });
41+
>result : Symbol(result, Decl(reverseMappedTypeInferFromItsProperties.ts, 12, 12))
42+
>createStructuredSelector : Symbol(createStructuredSelector, Decl(reverseMappedTypeInferFromItsProperties.ts, 2, 38))
43+
>mySelector : Symbol(mySelector, Decl(reverseMappedTypeInferFromItsProperties.ts, 12, 48))
44+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/compiler/reverseMappedTypeInferFromItsProperties.ts ===
2+
// repro from #29479
3+
4+
type Selector<S, R> = (state: S) => R;
5+
>Selector : Selector<S, R>
6+
>state : S
7+
8+
declare function createStructuredSelector<S, T>(selectors: {
9+
>createStructuredSelector : <S, T>(selectors: { [K in keyof T]: Selector<S, T[K]>; }) => Selector<S, T>
10+
>selectors : { [K in keyof T]: Selector<S, T[K]>; }
11+
12+
[K in keyof T]: Selector<S, T[K]>;
13+
}): Selector<S, T>;
14+
15+
type State = { foo: number };
16+
>State : { foo: number; }
17+
>foo : number
18+
19+
declare const mySelector: Selector<State, boolean>;
20+
>mySelector : Selector<State, boolean>
21+
22+
export const result = createStructuredSelector({ mySelector });
23+
>result : Selector<State, { mySelector: boolean; }>
24+
>createStructuredSelector({ mySelector }) : Selector<State, { mySelector: boolean; }>
25+
>createStructuredSelector : <S, T>(selectors: { [K in keyof T]: Selector<S, T[K]>; }) => Selector<S, T>
26+
>{ mySelector } : { mySelector: Selector<State, boolean>; }
27+
>mySelector : Selector<State, boolean>
28+

tests/cases/compiler/reverseMappedTypeContextualTypeNotCircular.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ declare function createStructuredSelector<S, T>(
77
const editable = () => ({});
88

99
const mapStateToProps = createStructuredSelector({
10-
editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<unknown, {}>'", _not_ a circularity error
10+
editable: (state: any, props: any) => editable(), // expect "Type '(state: any, props: any) => {}' is not assignable to type 'Selector<any, {}>'", _not_ a circularity error
1111
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// repro from #29479
5+
6+
type Selector<S, R> = (state: S) => R;
7+
8+
declare function createStructuredSelector<S, T>(selectors: {
9+
[K in keyof T]: Selector<S, T[K]>;
10+
}): Selector<S, T>;
11+
12+
type State = { foo: number };
13+
14+
declare const mySelector: Selector<State, boolean>;
15+
16+
export const result = createStructuredSelector({ mySelector });

0 commit comments

Comments
 (0)