Skip to content

No constraint for { [P in K]: XXX } where K is type variable #28170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6832,11 +6832,9 @@ namespace ts {
}
}
else {
// First, if the constraint type is a type parameter, obtain the base constraint. Then,
// if the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X.
// Finally, iterate over the constituents of the resulting iteration type.
const keyType = constraintType.flags & TypeFlags.InstantiableNonPrimitive ? getApparentType(constraintType) : constraintType;
const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>keyType).type)) : keyType;
// If the key type is a 'keyof X', obtain 'keyof C' where C is the base constraint of X.
// Then iterate over the constituents of the key type.
const iterationType = constraintType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>constraintType).type)) : constraintType;
forEachType(iterationType, addMemberForKeyType);
}
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
Expand Down
18 changes: 17 additions & 1 deletion tests/baselines/reference/mappedTypeErrors.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ tests/cases/conformance/types/mapped/mappedTypeErrors.ts(130,39): error TS2322:
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(136,16): error TS2322: Type 'T' is not assignable to type 'string | number | symbol'.
Type 'T' is not assignable to type 'symbol'.
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(136,21): error TS2536: Type 'P' cannot be used to index type 'T'.
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(148,17): error TS2339: Property 'foo' does not exist on type 'Pick<T, K>'.
tests/cases/conformance/types/mapped/mappedTypeErrors.ts(152,17): error TS2339: Property 'foo' does not exist on type 'Record<K, number>'.


==== tests/cases/conformance/types/mapped/mappedTypeErrors.ts (25 errors) ====
==== tests/cases/conformance/types/mapped/mappedTypeErrors.ts (27 errors) ====
interface Shape {
name: string;
width: number;
Expand Down Expand Up @@ -256,4 +258,18 @@ tests/cases/conformance/types/mapped/mappedTypeErrors.ts(136,21): error TS2536:
pf: {x: 7},
pt: {x: 7, y: false},
};

// Repro from #28170

function test1<T, K extends keyof T>(obj: Pick<T, K>) {
let x = obj.foo; // Error
~~~
!!! error TS2339: Property 'foo' does not exist on type 'Pick<T, K>'.
}

function test2<T, K extends keyof T>(obj: Record<K, number>) {
let x = obj.foo; // Error
~~~
!!! error TS2339: Property 'foo' does not exist on type 'Record<K, number>'.
}

19 changes: 19 additions & 0 deletions tests/baselines/reference/mappedTypeErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ let f: Foo2<O, 'x'> = {
pf: {x: 7},
pt: {x: 7, y: false},
};

// Repro from #28170

function test1<T, K extends keyof T>(obj: Pick<T, K>) {
let x = obj.foo; // Error
}

function test2<T, K extends keyof T>(obj: Record<K, number>) {
let x = obj.foo; // Error
}


//// [mappedTypeErrors.js]
Expand Down Expand Up @@ -222,6 +232,13 @@ var f = {
pf: { x: 7 },
pt: { x: 7, y: false }
};
// Repro from #28170
function test1(obj) {
var x = obj.foo; // Error
}
function test2(obj) {
var x = obj.foo; // Error
}


//// [mappedTypeErrors.d.ts]
Expand Down Expand Up @@ -300,3 +317,5 @@ declare type O = {
};
declare let o: O;
declare let f: Foo2<O, 'x'>;
declare function test1<T, K extends keyof T>(obj: Pick<T, K>): void;
declare function test2<T, K extends keyof T>(obj: Record<K, number>): void;
31 changes: 31 additions & 0 deletions tests/baselines/reference/mappedTypeErrors.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -540,3 +540,34 @@ let f: Foo2<O, 'x'> = {

};

// Repro from #28170

function test1<T, K extends keyof T>(obj: Pick<T, K>) {
>test1 : Symbol(test1, Decl(mappedTypeErrors.ts, 142, 2))
>T : Symbol(T, Decl(mappedTypeErrors.ts, 146, 15))
>K : Symbol(K, Decl(mappedTypeErrors.ts, 146, 17))
>T : Symbol(T, Decl(mappedTypeErrors.ts, 146, 15))
>obj : Symbol(obj, Decl(mappedTypeErrors.ts, 146, 37))
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypeErrors.ts, 146, 15))
>K : Symbol(K, Decl(mappedTypeErrors.ts, 146, 17))

let x = obj.foo; // Error
>x : Symbol(x, Decl(mappedTypeErrors.ts, 147, 7))
>obj : Symbol(obj, Decl(mappedTypeErrors.ts, 146, 37))
}

function test2<T, K extends keyof T>(obj: Record<K, number>) {
>test2 : Symbol(test2, Decl(mappedTypeErrors.ts, 148, 1))
>T : Symbol(T, Decl(mappedTypeErrors.ts, 150, 15))
>K : Symbol(K, Decl(mappedTypeErrors.ts, 150, 17))
>T : Symbol(T, Decl(mappedTypeErrors.ts, 150, 15))
>obj : Symbol(obj, Decl(mappedTypeErrors.ts, 150, 37))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>K : Symbol(K, Decl(mappedTypeErrors.ts, 150, 17))

let x = obj.foo; // Error
>x : Symbol(x, Decl(mappedTypeErrors.ts, 151, 7))
>obj : Symbol(obj, Decl(mappedTypeErrors.ts, 150, 37))
}

24 changes: 24 additions & 0 deletions tests/baselines/reference/mappedTypeErrors.types
Original file line number Diff line number Diff line change
Expand Up @@ -501,3 +501,27 @@ let f: Foo2<O, 'x'> = {

};

// Repro from #28170

function test1<T, K extends keyof T>(obj: Pick<T, K>) {
>test1 : <T, K extends keyof T>(obj: Pick<T, K>) => void
>obj : Pick<T, K>

let x = obj.foo; // Error
>x : any
>obj.foo : any
>obj : Pick<T, K>
>foo : any
}

function test2<T, K extends keyof T>(obj: Record<K, number>) {
>test2 : <T, K extends keyof T>(obj: Record<K, number>) => void
>obj : Record<K, number>

let x = obj.foo; // Error
>x : any
>obj.foo : any
>obj : Record<K, number>
>foo : any
}

10 changes: 10 additions & 0 deletions tests/cases/conformance/types/mapped/mappedTypeErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,13 @@ let f: Foo2<O, 'x'> = {
pf: {x: 7},
pt: {x: 7, y: false},
};

// Repro from #28170

function test1<T, K extends keyof T>(obj: Pick<T, K>) {
let x = obj.foo; // Error
}

function test2<T, K extends keyof T>(obj: Record<K, number>) {
let x = obj.foo; // Error
}