Merge pull request #13419 from Microsoft/fixKeyofWithIntersectionConstraint

Fix keyof with union or intersection constraint
This commit is contained in:
Anders Hejlsberg
2017-01-11 13:15:25 -10:00
committed by GitHub
6 changed files with 143 additions and 68 deletions

View File

@@ -4753,15 +4753,15 @@ namespace ts {
getPropertiesOfObjectType(type);
}
function getConstraintOfTypeVariable(type: TypeVariable): Type {
return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) : getBaseConstraintOfTypeVariable(type);
function getConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type {
return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(<TypeParameter>type) : getBaseConstraintOfType(type);
}
function getConstraintOfTypeParameter(typeParameter: TypeParameter): Type {
return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined;
}
function getBaseConstraintOfTypeVariable(type: TypeVariable): Type {
function getBaseConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type {
const constraint = getResolvedBaseConstraint(type);
return constraint !== noConstraintType && constraint !== circularConstraintType ? constraint : undefined;
}
@@ -4775,15 +4775,15 @@ namespace ts {
* type variable has no constraint, and the circularConstraintType singleton is returned if the constraint
* circularly references the type variable.
*/
function getResolvedBaseConstraint(type: TypeVariable): Type {
function getResolvedBaseConstraint(type: TypeVariable | UnionOrIntersectionType): Type {
let typeStack: Type[];
let circular: boolean;
if (!type.resolvedApparentType) {
if (!type.resolvedBaseConstraint) {
typeStack = [];
const constraint = getBaseConstraint(type);
type.resolvedApparentType = circular ? circularConstraintType : getTypeWithThisArgument(constraint || noConstraintType, type);
type.resolvedBaseConstraint = circular ? circularConstraintType : getTypeWithThisArgument(constraint || noConstraintType, type);
}
return type.resolvedApparentType;
return type.resolvedBaseConstraint;
function getBaseConstraint(t: Type): Type {
if (contains(typeStack, t)) {
@@ -4834,7 +4834,7 @@ namespace ts {
* type itself. Note that the apparent type of a union type is the union type itself.
*/
function getApparentType(type: Type): Type {
const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfTypeVariable(<TypeVariable>type) || emptyObjectType : type;
const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfType(<TypeVariable>type) || emptyObjectType : type;
return t.flags & TypeFlags.StringLike ? globalStringType :
t.flags & TypeFlags.NumberLike ? globalNumberType :
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
@@ -7409,16 +7409,6 @@ namespace ts {
}
}
}
else {
// Given a type parameter K with a constraint keyof T, a type S is
// assignable to K if S is assignable to keyof T.
const constraint = getConstraintOfTypeParameter(<TypeParameter>target);
if (constraint && constraint.flags & TypeFlags.Index) {
if (result = isRelatedTo(source, constraint, reportErrors)) {
return result;
}
}
}
}
else if (target.flags & TypeFlags.Index) {
// A keyof S is related to a keyof T if T is related to S.
@@ -7427,14 +7417,12 @@ namespace ts {
return result;
}
}
// Given a type variable T with a constraint C, a type S is assignable to
// keyof T if S is assignable to keyof C.
if ((<IndexType>target).type.flags & TypeFlags.TypeVariable) {
const constraint = getConstraintOfTypeVariable(<TypeVariable>(<IndexType>target).type);
if (constraint) {
if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) {
return result;
}
// A type S is assignable to keyof T if S is assignable to keyof C, where C is the
// constraint of T.
const constraint = getConstraintOfType((<IndexType>target).type);
if (constraint) {
if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) {
return result;
}
}
}
@@ -7448,7 +7436,7 @@ namespace ts {
}
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
// A is the apparent type of S.
const constraint = getBaseConstraintOfTypeVariable(<IndexedAccessType>target);
const constraint = getBaseConstraintOfType(<IndexedAccessType>target);
if (constraint) {
if (result = isRelatedTo(source, constraint, reportErrors)) {
errorInfo = saveErrorInfo;
@@ -7488,7 +7476,7 @@ namespace ts {
else if (source.flags & TypeFlags.IndexedAccess) {
// A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
// A is the apparent type of S.
const constraint = getBaseConstraintOfTypeVariable(<IndexedAccessType>source);
const constraint = getBaseConstraintOfType(<IndexedAccessType>source);
if (constraint) {
if (result = isRelatedTo(constraint, target, reportErrors)) {
errorInfo = saveErrorInfo;
@@ -15207,7 +15195,7 @@ namespace ts {
function isLiteralContextualType(contextualType: Type) {
if (contextualType) {
if (contextualType.flags & TypeFlags.TypeVariable) {
const constraint = getBaseConstraintOfTypeVariable(<TypeVariable>contextualType) || emptyObjectType;
const constraint = getBaseConstraintOfType(<TypeVariable>contextualType) || emptyObjectType;
// If the type parameter is constrained to the base primitive type we're checking for,
// consider this a literal context. For example, given a type parameter 'T extends string',
// this causes us to infer string literal types for T.

View File

@@ -2923,6 +2923,8 @@ namespace ts {
/* @internal */
resolvedIndexType: IndexType;
/* @internal */
resolvedBaseConstraint: Type;
/* @internal */
couldContainTypeVariables: boolean;
}
@@ -2982,7 +2984,7 @@ namespace ts {
export interface TypeVariable extends Type {
/* @internal */
resolvedApparentType: Type;
resolvedBaseConstraint: Type;
/* @internal */
resolvedIndexType: IndexType;
}

View File

@@ -464,6 +464,16 @@ function addToMyThingy<S extends KeyTypes>(key: S) {
MyThingy[key].push("a");
}
// Repro from #13102
type Handler<T> = {
onChange: (name: keyof T) => void;
};
function onChangeGenericFunction<T>(handler: Handler<T & {preset: number}>) {
handler.onChange('preset')
}
// Repro from #13285
function updateIds<T extends Record<K, string>, K extends string>(
@@ -801,6 +811,9 @@ var MyThingy;
function addToMyThingy(key) {
MyThingy[key].push("a");
}
function onChangeGenericFunction(handler) {
handler.onChange('preset');
}
// Repro from #13285
function updateIds(obj, idFields, idMapping) {
for (var _i = 0, idFields_1 = idFields; _i < idFields_1.length; _i++) {
@@ -1034,6 +1047,12 @@ declare let MyThingy: {
[key in KeyTypes]: string[];
};
declare function addToMyThingy<S extends KeyTypes>(key: S): void;
declare type Handler<T> = {
onChange: (name: keyof T) => void;
};
declare function onChangeGenericFunction<T>(handler: Handler<T & {
preset: number;
}>): void;
declare function updateIds<T extends Record<K, string>, K extends string>(obj: T, idFields: K[], idMapping: {
[oldId: string]: string;
}): Record<K, string>;

View File

@@ -1699,83 +1699,110 @@ function addToMyThingy<S extends KeyTypes>(key: S) {
>push : Symbol(Array.push, Decl(lib.d.ts, --, --))
}
// Repro from #13102
type Handler<T> = {
>Handler : Symbol(Handler, Decl(keyofAndIndexedAccess.ts, 463, 1))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 467, 13))
onChange: (name: keyof T) => void;
>onChange : Symbol(onChange, Decl(keyofAndIndexedAccess.ts, 467, 19))
>name : Symbol(name, Decl(keyofAndIndexedAccess.ts, 468, 15))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 467, 13))
};
function onChangeGenericFunction<T>(handler: Handler<T & {preset: number}>) {
>onChangeGenericFunction : Symbol(onChangeGenericFunction, Decl(keyofAndIndexedAccess.ts, 469, 2))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 471, 33))
>handler : Symbol(handler, Decl(keyofAndIndexedAccess.ts, 471, 36))
>Handler : Symbol(Handler, Decl(keyofAndIndexedAccess.ts, 463, 1))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 471, 33))
>preset : Symbol(preset, Decl(keyofAndIndexedAccess.ts, 471, 58))
handler.onChange('preset')
>handler.onChange : Symbol(onChange, Decl(keyofAndIndexedAccess.ts, 467, 19))
>handler : Symbol(handler, Decl(keyofAndIndexedAccess.ts, 471, 36))
>onChange : Symbol(onChange, Decl(keyofAndIndexedAccess.ts, 467, 19))
}
// Repro from #13285
function updateIds<T extends Record<K, string>, K extends string>(
>updateIds : Symbol(updateIds, Decl(keyofAndIndexedAccess.ts, 463, 1))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 467, 19))
>updateIds : Symbol(updateIds, Decl(keyofAndIndexedAccess.ts, 473, 1))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 477, 19))
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 467, 47))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 467, 47))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 477, 47))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 477, 47))
obj: T,
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 467, 66))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 467, 19))
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 477, 66))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 477, 19))
idFields: K[],
>idFields : Symbol(idFields, Decl(keyofAndIndexedAccess.ts, 468, 11))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 467, 47))
>idFields : Symbol(idFields, Decl(keyofAndIndexedAccess.ts, 478, 11))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 477, 47))
idMapping: { [oldId: string]: string }
>idMapping : Symbol(idMapping, Decl(keyofAndIndexedAccess.ts, 469, 18))
>oldId : Symbol(oldId, Decl(keyofAndIndexedAccess.ts, 470, 18))
>idMapping : Symbol(idMapping, Decl(keyofAndIndexedAccess.ts, 479, 18))
>oldId : Symbol(oldId, Decl(keyofAndIndexedAccess.ts, 480, 18))
): Record<K, string> {
>Record : Symbol(Record, Decl(lib.d.ts, --, --))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 467, 47))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 477, 47))
for (const idField of idFields) {
>idField : Symbol(idField, Decl(keyofAndIndexedAccess.ts, 472, 14))
>idFields : Symbol(idFields, Decl(keyofAndIndexedAccess.ts, 468, 11))
>idField : Symbol(idField, Decl(keyofAndIndexedAccess.ts, 482, 14))
>idFields : Symbol(idFields, Decl(keyofAndIndexedAccess.ts, 478, 11))
const newId = idMapping[obj[idField]];
>newId : Symbol(newId, Decl(keyofAndIndexedAccess.ts, 473, 13))
>idMapping : Symbol(idMapping, Decl(keyofAndIndexedAccess.ts, 469, 18))
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 467, 66))
>idField : Symbol(idField, Decl(keyofAndIndexedAccess.ts, 472, 14))
>newId : Symbol(newId, Decl(keyofAndIndexedAccess.ts, 483, 13))
>idMapping : Symbol(idMapping, Decl(keyofAndIndexedAccess.ts, 479, 18))
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 477, 66))
>idField : Symbol(idField, Decl(keyofAndIndexedAccess.ts, 482, 14))
if (newId) {
>newId : Symbol(newId, Decl(keyofAndIndexedAccess.ts, 473, 13))
>newId : Symbol(newId, Decl(keyofAndIndexedAccess.ts, 483, 13))
obj[idField] = newId;
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 467, 66))
>idField : Symbol(idField, Decl(keyofAndIndexedAccess.ts, 472, 14))
>newId : Symbol(newId, Decl(keyofAndIndexedAccess.ts, 473, 13))
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 477, 66))
>idField : Symbol(idField, Decl(keyofAndIndexedAccess.ts, 482, 14))
>newId : Symbol(newId, Decl(keyofAndIndexedAccess.ts, 483, 13))
}
}
return obj;
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 467, 66))
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 477, 66))
}
// Repro from #13285
function updateIds2<T extends { [x: string]: string }, K extends keyof T>(
>updateIds2 : Symbol(updateIds2, Decl(keyofAndIndexedAccess.ts, 479, 1))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 483, 20))
>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 483, 33))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 483, 54))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 483, 20))
>updateIds2 : Symbol(updateIds2, Decl(keyofAndIndexedAccess.ts, 489, 1))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 493, 20))
>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 493, 33))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 493, 54))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 493, 20))
obj: T,
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 483, 74))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 483, 20))
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 493, 74))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 493, 20))
key: K,
>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 484, 11))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 483, 54))
>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 494, 11))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 493, 54))
stringMap: { [oldId: string]: string }
>stringMap : Symbol(stringMap, Decl(keyofAndIndexedAccess.ts, 485, 11))
>oldId : Symbol(oldId, Decl(keyofAndIndexedAccess.ts, 486, 18))
>stringMap : Symbol(stringMap, Decl(keyofAndIndexedAccess.ts, 495, 11))
>oldId : Symbol(oldId, Decl(keyofAndIndexedAccess.ts, 496, 18))
) {
var x = obj[key];
>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 488, 7))
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 483, 74))
>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 484, 11))
>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 498, 7))
>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 493, 74))
>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 494, 11))
stringMap[x]; // Should be OK.
>stringMap : Symbol(stringMap, Decl(keyofAndIndexedAccess.ts, 485, 11))
>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 488, 7))
>stringMap : Symbol(stringMap, Decl(keyofAndIndexedAccess.ts, 495, 11))
>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 498, 7))
}

View File

@@ -2013,6 +2013,35 @@ function addToMyThingy<S extends KeyTypes>(key: S) {
>"a" : "a"
}
// Repro from #13102
type Handler<T> = {
>Handler : Handler<T>
>T : T
onChange: (name: keyof T) => void;
>onChange : (name: keyof T) => void
>name : keyof T
>T : T
};
function onChangeGenericFunction<T>(handler: Handler<T & {preset: number}>) {
>onChangeGenericFunction : <T>(handler: Handler<T & { preset: number; }>) => void
>T : T
>handler : Handler<T & { preset: number; }>
>Handler : Handler<T>
>T : T
>preset : number
handler.onChange('preset')
>handler.onChange('preset') : void
>handler.onChange : (name: keyof (T & { preset: number; })) => void
>handler : Handler<T & { preset: number; }>
>onChange : (name: keyof (T & { preset: number; })) => void
>'preset' : "preset"
}
// Repro from #13285
function updateIds<T extends Record<K, string>, K extends string>(

View File

@@ -465,6 +465,16 @@ function addToMyThingy<S extends KeyTypes>(key: S) {
MyThingy[key].push("a");
}
// Repro from #13102
type Handler<T> = {
onChange: (name: keyof T) => void;
};
function onChangeGenericFunction<T>(handler: Handler<T & {preset: number}>) {
handler.onChange('preset')
}
// Repro from #13285
function updateIds<T extends Record<K, string>, K extends string>(