Skip to content

Commit

Permalink
Merge pull request #19319 from Microsoft/fixMappedTypeInferenceErrors
Browse files Browse the repository at this point in the history
Fix mapped type inference errors
  • Loading branch information
ahejlsberg authored Oct 20, 2017
2 parents 8adbcef + f8d9079 commit 8a22767
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 12 deletions.
16 changes: 7 additions & 9 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10665,29 +10665,27 @@ namespace ts {
const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional;
const members = createSymbolTable();
for (const prop of properties) {
const inferredPropType = inferTargetType(getTypeOfSymbol(prop));
if (!inferredPropType) {
const propType = getTypeOfSymbol(prop);
// If any property contains context sensitive functions that have been skipped, the source type
// is incomplete and we can't infer a meaningful input type.
if (propType.flags & TypeFlags.ContainsAnyFunctionType) {
return undefined;
}
const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName);
inferredProp.checkFlags = readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0;
inferredProp.declarations = prop.declarations;
inferredProp.type = inferredPropType;
inferredProp.type = inferTargetType(propType);
members.set(prop.escapedName, inferredProp);
}
if (indexInfo) {
const inferredIndexType = inferTargetType(indexInfo.type);
if (!inferredIndexType) {
return undefined;
}
indexInfo = createIndexInfo(inferredIndexType, readonlyMask && indexInfo.isReadonly);
indexInfo = createIndexInfo(inferTargetType(indexInfo.type), readonlyMask && indexInfo.isReadonly);
}
return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined);

function inferTargetType(sourceType: Type): Type {
inference.candidates = undefined;
inferTypes(inferences, sourceType, templateType);
return inference.candidates && getUnionType(inference.candidates, /*subtypeReduction*/ true);
return inference.candidates ? getUnionType(inference.candidates, /*subtypeReduction*/ true) : emptyObjectType;
}
}

Expand Down
4 changes: 3 additions & 1 deletion tests/baselines/reference/keyofAndIndexedAccess.js
Original file line number Diff line number Diff line change
Expand Up @@ -1101,7 +1101,9 @@ declare type Handlers<T> = {
[K in keyof T]: (t: T[K]) => void;
};
declare function on<T>(handlerHash: Handlers<T>): T;
declare var hashOfEmpty1: {};
declare var hashOfEmpty1: {
test: {};
};
declare var hashOfEmpty2: {
test: boolean;
};
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/keyofAndIndexedAccess.types
Original file line number Diff line number Diff line change
Expand Up @@ -1793,8 +1793,8 @@ declare function on<T>(handlerHash: Handlers<T>): T
>T : T

var hashOfEmpty1 = on({ test: () => {} }); // {}
>hashOfEmpty1 : {}
>on({ test: () => {} }) : {}
>hashOfEmpty1 : { test: {}; }
>on({ test: () => {} }) : { test: {}; }
>on : <T>(handlerHash: Handlers<T>) => T
>{ test: () => {} } : { test: () => void; }
>test : () => void
Expand Down
44 changes: 44 additions & 0 deletions tests/baselines/reference/mappedTypeInferenceErrors.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
tests/cases/conformance/types/mapped/mappedTypeInferenceErrors.ts(9,5): error TS2345: Argument of type '{ props: { x: number; y: number; }; computed: { bar(): number; baz: number; }; }' is not assignable to parameter of type '{ props: { x: number; y: number; }; computed: ComputedOf<{ bar: number; baz: {}; }>; } & ThisType<{ x: number; y: number; } & { bar: number; baz: {}; }>'.
Type '{ props: { x: number; y: number; }; computed: { bar(): number; baz: number; }; }' is not assignable to type '{ props: { x: number; y: number; }; computed: ComputedOf<{ bar: number; baz: {}; }>; }'.
Types of property 'computed' are incompatible.
Type '{ bar(): number; baz: number; }' is not assignable to type 'ComputedOf<{ bar: number; baz: {}; }>'.
Types of property 'baz' are incompatible.
Type 'number' is not assignable to type '() => {}'.


==== tests/cases/conformance/types/mapped/mappedTypeInferenceErrors.ts (1 errors) ====
// Repro from #19316

type ComputedOf<T> = {
[K in keyof T]: () => T[K];
}

declare function foo<P, C>(options: { props: P, computed: ComputedOf<C> } & ThisType<P & C>): void;

foo({
~
props: { x: 10, y: 20 },
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
computed: {
~~~~~~~~~~~~~~~
bar(): number {
~~~~~~~~~~~~~~~~~~~~~~~
let z = this.bar;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
return 42;
~~~~~~~~~~~~~~~~~~~~~~
},
~~~~~~~~~~
baz: 42
~~~~~~~~~~~~~~~
}
~~~~~
});
~
!!! error TS2345: Argument of type '{ props: { x: number; y: number; }; computed: { bar(): number; baz: number; }; }' is not assignable to parameter of type '{ props: { x: number; y: number; }; computed: ComputedOf<{ bar: number; baz: {}; }>; } & ThisType<{ x: number; y: number; } & { bar: number; baz: {}; }>'.
!!! error TS2345: Type '{ props: { x: number; y: number; }; computed: { bar(): number; baz: number; }; }' is not assignable to type '{ props: { x: number; y: number; }; computed: ComputedOf<{ bar: number; baz: {}; }>; }'.
!!! error TS2345: Types of property 'computed' are incompatible.
!!! error TS2345: Type '{ bar(): number; baz: number; }' is not assignable to type 'ComputedOf<{ bar: number; baz: {}; }>'.
!!! error TS2345: Types of property 'baz' are incompatible.
!!! error TS2345: Type 'number' is not assignable to type '() => {}'.

34 changes: 34 additions & 0 deletions tests/baselines/reference/mappedTypeInferenceErrors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//// [mappedTypeInferenceErrors.ts]
// Repro from #19316

type ComputedOf<T> = {
[K in keyof T]: () => T[K];
}

declare function foo<P, C>(options: { props: P, computed: ComputedOf<C> } & ThisType<P & C>): void;

foo({
props: { x: 10, y: 20 },
computed: {
bar(): number {
let z = this.bar;
return 42;
},
baz: 42
}
});


//// [mappedTypeInferenceErrors.js]
"use strict";
// Repro from #19316
foo({
props: { x: 10, y: 20 },
computed: {
bar: function () {
var z = this.bar;
return 42;
},
baz: 42
}
});
55 changes: 55 additions & 0 deletions tests/baselines/reference/mappedTypeInferenceErrors.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
=== tests/cases/conformance/types/mapped/mappedTypeInferenceErrors.ts ===
// Repro from #19316

type ComputedOf<T> = {
>ComputedOf : Symbol(ComputedOf, Decl(mappedTypeInferenceErrors.ts, 0, 0))
>T : Symbol(T, Decl(mappedTypeInferenceErrors.ts, 2, 16))

[K in keyof T]: () => T[K];
>K : Symbol(K, Decl(mappedTypeInferenceErrors.ts, 3, 5))
>T : Symbol(T, Decl(mappedTypeInferenceErrors.ts, 2, 16))
>T : Symbol(T, Decl(mappedTypeInferenceErrors.ts, 2, 16))
>K : Symbol(K, Decl(mappedTypeInferenceErrors.ts, 3, 5))
}

declare function foo<P, C>(options: { props: P, computed: ComputedOf<C> } & ThisType<P & C>): void;
>foo : Symbol(foo, Decl(mappedTypeInferenceErrors.ts, 4, 1))
>P : Symbol(P, Decl(mappedTypeInferenceErrors.ts, 6, 21))
>C : Symbol(C, Decl(mappedTypeInferenceErrors.ts, 6, 23))
>options : Symbol(options, Decl(mappedTypeInferenceErrors.ts, 6, 27))
>props : Symbol(props, Decl(mappedTypeInferenceErrors.ts, 6, 37))
>P : Symbol(P, Decl(mappedTypeInferenceErrors.ts, 6, 21))
>computed : Symbol(computed, Decl(mappedTypeInferenceErrors.ts, 6, 47))
>ComputedOf : Symbol(ComputedOf, Decl(mappedTypeInferenceErrors.ts, 0, 0))
>C : Symbol(C, Decl(mappedTypeInferenceErrors.ts, 6, 23))
>ThisType : Symbol(ThisType, Decl(lib.d.ts, --, --))
>P : Symbol(P, Decl(mappedTypeInferenceErrors.ts, 6, 21))
>C : Symbol(C, Decl(mappedTypeInferenceErrors.ts, 6, 23))

foo({
>foo : Symbol(foo, Decl(mappedTypeInferenceErrors.ts, 4, 1))

props: { x: 10, y: 20 },
>props : Symbol(props, Decl(mappedTypeInferenceErrors.ts, 8, 5))
>x : Symbol(x, Decl(mappedTypeInferenceErrors.ts, 9, 12))
>y : Symbol(y, Decl(mappedTypeInferenceErrors.ts, 9, 19))

computed: {
>computed : Symbol(computed, Decl(mappedTypeInferenceErrors.ts, 9, 28))

bar(): number {
>bar : Symbol(bar, Decl(mappedTypeInferenceErrors.ts, 10, 15))

let z = this.bar;
>z : Symbol(z, Decl(mappedTypeInferenceErrors.ts, 12, 15))
>this.bar : Symbol(bar, Decl(mappedTypeInferenceErrors.ts, 10, 15))
>this : Symbol(__object, Decl(mappedTypeInferenceErrors.ts, 10, 13))
>bar : Symbol(bar, Decl(mappedTypeInferenceErrors.ts, 10, 15))

return 42;
},
baz: 42
>baz : Symbol(baz, Decl(mappedTypeInferenceErrors.ts, 14, 10))
}
});

64 changes: 64 additions & 0 deletions tests/baselines/reference/mappedTypeInferenceErrors.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
=== tests/cases/conformance/types/mapped/mappedTypeInferenceErrors.ts ===
// Repro from #19316

type ComputedOf<T> = {
>ComputedOf : ComputedOf<T>
>T : T

[K in keyof T]: () => T[K];
>K : K
>T : T
>T : T
>K : K
}

declare function foo<P, C>(options: { props: P, computed: ComputedOf<C> } & ThisType<P & C>): void;
>foo : <P, C>(options: { props: P; computed: ComputedOf<C>; } & ThisType<P & C>) => void
>P : P
>C : C
>options : { props: P; computed: ComputedOf<C>; } & ThisType<P & C>
>props : P
>P : P
>computed : ComputedOf<C>
>ComputedOf : ComputedOf<T>
>C : C
>ThisType : ThisType<T>
>P : P
>C : C

foo({
>foo({ props: { x: 10, y: 20 }, computed: { bar(): number { let z = this.bar; return 42; }, baz: 42 }}) : any
>foo : <P, C>(options: { props: P; computed: ComputedOf<C>; } & ThisType<P & C>) => void
>{ props: { x: 10, y: 20 }, computed: { bar(): number { let z = this.bar; return 42; }, baz: 42 }} : { props: { x: number; y: number; }; computed: { bar(): number; baz: number; }; }

props: { x: 10, y: 20 },
>props : { x: number; y: number; }
>{ x: 10, y: 20 } : { x: number; y: number; }
>x : number
>10 : 10
>y : number
>20 : 20

computed: {
>computed : { bar(): number; baz: number; }
>{ bar(): number { let z = this.bar; return 42; }, baz: 42 } : { bar(): number; baz: number; }

bar(): number {
>bar : () => number

let z = this.bar;
>z : () => number
>this.bar : () => number
>this : { bar(): number; baz: number; }
>bar : () => number

return 42;
>42 : 42

},
baz: 42
>baz : number
>42 : 42
}
});

20 changes: 20 additions & 0 deletions tests/cases/conformance/types/mapped/mappedTypeInferenceErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @strict: true

// Repro from #19316

type ComputedOf<T> = {
[K in keyof T]: () => T[K];
}

declare function foo<P, C>(options: { props: P, computed: ComputedOf<C> } & ThisType<P & C>): void;

foo({
props: { x: 10, y: 20 },
computed: {
bar(): number {
let z = this.bar;
return 42;
},
baz: 42
}
});

0 comments on commit 8a22767

Please sign in to comment.