From 86f69f13fa84e859eceb2b3c8abb60cdd400cd03 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 30 Nov 2016 10:35:44 -0800 Subject: [PATCH 01/17] Add test for the decorated method beginning with underscore --- .../decoratorWithUnderscoreMethod.js | 37 +++++++++++++++ .../decoratorWithUnderscoreMethod.symbols | 42 +++++++++++++++++ .../decoratorWithUnderscoreMethod.types | 46 +++++++++++++++++++ .../compiler/decoratorWithUnderscoreMethod.ts | 18 ++++++++ 4 files changed, 143 insertions(+) create mode 100644 tests/baselines/reference/decoratorWithUnderscoreMethod.js create mode 100644 tests/baselines/reference/decoratorWithUnderscoreMethod.symbols create mode 100644 tests/baselines/reference/decoratorWithUnderscoreMethod.types create mode 100644 tests/cases/compiler/decoratorWithUnderscoreMethod.ts diff --git a/tests/baselines/reference/decoratorWithUnderscoreMethod.js b/tests/baselines/reference/decoratorWithUnderscoreMethod.js new file mode 100644 index 0000000000000..03037980ee7f4 --- /dev/null +++ b/tests/baselines/reference/decoratorWithUnderscoreMethod.js @@ -0,0 +1,37 @@ +//// [decoratorWithUnderscoreMethod.ts] + +declare var console : { log(arg: string): void }; +function dec(): Function { + return function (target: any, propKey: string, descr: PropertyDescriptor): void { + console.log(target[propKey]); + //logs undefined + //propKey has three underscores as prefix, but the method has only two underscores + }; +} + +class A { + @dec() + private __foo(bar: string): void { + // do something with bar + } +} + +//// [decoratorWithUnderscoreMethod.js] +function dec() { + return function (target, propKey, descr) { + console.log(target[propKey]); + //logs undefined + //propKey has three underscores as prefix, but the method has only two underscores + }; +} +var A = (function () { + function A() { + } + A.prototype.__foo = function (bar) { + // do something with bar + }; + return A; +}()); +__decorate([ + dec() +], A.prototype, "___foo"); diff --git a/tests/baselines/reference/decoratorWithUnderscoreMethod.symbols b/tests/baselines/reference/decoratorWithUnderscoreMethod.symbols new file mode 100644 index 0000000000000..a512bde71f6fe --- /dev/null +++ b/tests/baselines/reference/decoratorWithUnderscoreMethod.symbols @@ -0,0 +1,42 @@ +=== tests/cases/compiler/decoratorWithUnderscoreMethod.ts === + +declare var console : { log(arg: string): void }; +>console : Symbol(console, Decl(decoratorWithUnderscoreMethod.ts, 1, 11)) +>log : Symbol(log, Decl(decoratorWithUnderscoreMethod.ts, 1, 23)) +>arg : Symbol(arg, Decl(decoratorWithUnderscoreMethod.ts, 1, 28)) + +function dec(): Function { +>dec : Symbol(dec, Decl(decoratorWithUnderscoreMethod.ts, 1, 49)) +>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + return function (target: any, propKey: string, descr: PropertyDescriptor): void { +>target : Symbol(target, Decl(decoratorWithUnderscoreMethod.ts, 3, 21)) +>propKey : Symbol(propKey, Decl(decoratorWithUnderscoreMethod.ts, 3, 33)) +>descr : Symbol(descr, Decl(decoratorWithUnderscoreMethod.ts, 3, 50)) +>PropertyDescriptor : Symbol(PropertyDescriptor, Decl(lib.d.ts, --, --)) + + console.log(target[propKey]); +>console.log : Symbol(log, Decl(decoratorWithUnderscoreMethod.ts, 1, 23)) +>console : Symbol(console, Decl(decoratorWithUnderscoreMethod.ts, 1, 11)) +>log : Symbol(log, Decl(decoratorWithUnderscoreMethod.ts, 1, 23)) +>target : Symbol(target, Decl(decoratorWithUnderscoreMethod.ts, 3, 21)) +>propKey : Symbol(propKey, Decl(decoratorWithUnderscoreMethod.ts, 3, 33)) + + //logs undefined + //propKey has three underscores as prefix, but the method has only two underscores + }; +} + +class A { +>A : Symbol(A, Decl(decoratorWithUnderscoreMethod.ts, 8, 1)) + + @dec() +>dec : Symbol(dec, Decl(decoratorWithUnderscoreMethod.ts, 1, 49)) + + private __foo(bar: string): void { +>__foo : Symbol(A.__foo, Decl(decoratorWithUnderscoreMethod.ts, 10, 9)) +>bar : Symbol(bar, Decl(decoratorWithUnderscoreMethod.ts, 12, 18)) + + // do something with bar + } +} diff --git a/tests/baselines/reference/decoratorWithUnderscoreMethod.types b/tests/baselines/reference/decoratorWithUnderscoreMethod.types new file mode 100644 index 0000000000000..1ce08dc8a9eed --- /dev/null +++ b/tests/baselines/reference/decoratorWithUnderscoreMethod.types @@ -0,0 +1,46 @@ +=== tests/cases/compiler/decoratorWithUnderscoreMethod.ts === + +declare var console : { log(arg: string): void }; +>console : { log(arg: string): void; } +>log : (arg: string) => void +>arg : string + +function dec(): Function { +>dec : () => Function +>Function : Function + + return function (target: any, propKey: string, descr: PropertyDescriptor): void { +>function (target: any, propKey: string, descr: PropertyDescriptor): void { console.log(target[propKey]); //logs undefined //propKey has three underscores as prefix, but the method has only two underscores } : (target: any, propKey: string, descr: PropertyDescriptor) => void +>target : any +>propKey : string +>descr : PropertyDescriptor +>PropertyDescriptor : PropertyDescriptor + + console.log(target[propKey]); +>console.log(target[propKey]) : void +>console.log : (arg: string) => void +>console : { log(arg: string): void; } +>log : (arg: string) => void +>target[propKey] : any +>target : any +>propKey : string + + //logs undefined + //propKey has three underscores as prefix, but the method has only two underscores + }; +} + +class A { +>A : A + + @dec() +>dec() : Function +>dec : () => Function + + private __foo(bar: string): void { +>__foo : (bar: string) => void +>bar : string + + // do something with bar + } +} diff --git a/tests/cases/compiler/decoratorWithUnderscoreMethod.ts b/tests/cases/compiler/decoratorWithUnderscoreMethod.ts new file mode 100644 index 0000000000000..e6551c9128484 --- /dev/null +++ b/tests/cases/compiler/decoratorWithUnderscoreMethod.ts @@ -0,0 +1,18 @@ +// @noemithelpers: true +// @experimentaldecorators: true + +declare var console : { log(arg: string): void }; +function dec(): Function { + return function (target: any, propKey: string, descr: PropertyDescriptor): void { + console.log(target[propKey]); + //logs undefined + //propKey has three underscores as prefix, but the method has only two underscores + }; +} + +class A { + @dec() + private __foo(bar: string): void { + // do something with bar + } +} \ No newline at end of file From e5b5fe1bd7784e63cc57214eafe845c222af0891 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 30 Nov 2016 10:51:35 -0800 Subject: [PATCH 02/17] When creating string literal for property access, unescape the identifier text Fixes #12562 --- src/compiler/transformers/ts.ts | 2 +- tests/baselines/reference/decoratorWithUnderscoreMethod.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 52717a9499676..6af8a2635993f 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1905,7 +1905,7 @@ namespace ts { : (name).expression; } else if (isIdentifier(name)) { - return createLiteral(name.text); + return createLiteral(unescapeIdentifier(name.text)); } else { return getSynthesizedClone(name); diff --git a/tests/baselines/reference/decoratorWithUnderscoreMethod.js b/tests/baselines/reference/decoratorWithUnderscoreMethod.js index 03037980ee7f4..bf79890263bc7 100644 --- a/tests/baselines/reference/decoratorWithUnderscoreMethod.js +++ b/tests/baselines/reference/decoratorWithUnderscoreMethod.js @@ -34,4 +34,4 @@ var A = (function () { }()); __decorate([ dec() -], A.prototype, "___foo"); +], A.prototype, "__foo"); From edb376db013d1334deeead800ceb9574f412d387 Mon Sep 17 00:00:00 2001 From: arusakov Date: Fri, 2 Dec 2016 18:34:48 +0300 Subject: [PATCH 03/17] Update cli --target description (es2016 and 2017) --- src/compiler/commandLineParser.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 3c6a86134eafa..e9f9ff210a9a0 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -272,7 +272,7 @@ namespace ts { "es2017": ScriptTarget.ES2017, "esnext": ScriptTarget.ESNext, }), - description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES2015, + description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_ES2015_ES2016_ES2017_or_ESNEXT, paramType: Diagnostics.VERSION, }, { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 5f49c54a2af22..4d42c77e5669b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2473,7 +2473,7 @@ "category": "Message", "code": 6012 }, - "Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES2015'": { + "Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'": { "category": "Message", "code": 6015 }, From 3b1d6c969d90b8d41b5bdfce13bc1955512b53de Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 2 Dec 2016 14:29:03 -0800 Subject: [PATCH 04/17] Treat indexed access types T[K] as type variables --- src/compiler/checker.ts | 163 +++++++++++++++++++++------------------- src/compiler/types.ts | 25 +++--- 2 files changed, 100 insertions(+), 88 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 02cde0d4412fc..81f982e4dc069 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4513,10 +4513,10 @@ namespace ts { setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined); // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type, // and T as the template type. If K is of the form 'keyof S', the mapped type and S are - // isomorphic and we copy property modifiers from corresponding properties in S. + // homomorphic and we copy property modifiers from corresponding properties in S. const typeParameter = getTypeParameterFromMappedType(type); const constraintType = getConstraintTypeFromMappedType(type); - const isomorphicType = getIsomorphicTypeFromMappedType(type); + const homomorphicType = getHomomorphicTypeFromMappedType(type); const templateType = getTemplateTypeFromMappedType(type); const templateReadonly = !!type.declaration.readonlyToken; const templateOptional = !!type.declaration.questionToken; @@ -4536,11 +4536,11 @@ namespace ts { // Otherwise, for type string create a string index signature. if (t.flags & TypeFlags.StringLiteral) { const propName = (t).text; - const isomorphicProp = isomorphicType && getPropertyOfType(isomorphicType, propName); - const isOptional = templateOptional || !!(isomorphicProp && isomorphicProp.flags & SymbolFlags.Optional); + const homomorphicProp = homomorphicType && getPropertyOfType(homomorphicType, propName); + const isOptional = templateOptional || !!(homomorphicProp && homomorphicProp.flags & SymbolFlags.Optional); const prop = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName); prop.type = propType; - prop.isReadonly = templateReadonly || isomorphicProp && isReadonlySymbol(isomorphicProp); + prop.isReadonly = templateReadonly || homomorphicProp && isReadonlySymbol(homomorphicProp); members[propName] = prop; } else if (t.flags & TypeFlags.String) { @@ -4567,7 +4567,7 @@ namespace ts { unknownType); } - function getIsomorphicTypeFromMappedType(type: MappedType) { + function getHomomorphicTypeFromMappedType(type: MappedType) { const constraint = getConstraintDeclaration(getTypeParameterFromMappedType(type)); return constraint.kind === SyntaxKind.TypeOperator ? instantiateType(getTypeFromTypeNode((constraint).type), type.mapper || identityMapper) : undefined; } @@ -5912,7 +5912,7 @@ namespace ts { return links.resolvedType; } - function getIndexTypeForTypeParameter(type: TypeParameter) { + function getIndexTypeForTypeVariable(type: TypeVariable) { if (!type.resolvedIndexType) { type.resolvedIndexType = createType(TypeFlags.Index); type.resolvedIndexType.type = type; @@ -5931,7 +5931,7 @@ namespace ts { } function getIndexType(type: Type): Type { - return type.flags & TypeFlags.TypeParameter ? getIndexTypeForTypeParameter(type) : + return type.flags & TypeFlags.TypeVariable ? getIndexTypeForTypeVariable(type) : getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(type) : type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromPropertyNames(type); @@ -6032,13 +6032,14 @@ namespace ts { } function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) { - if (indexType.flags & TypeFlags.TypeParameter || - objectType.flags & TypeFlags.TypeParameter && indexType.flags & TypeFlags.Index || + if (indexType.flags & TypeFlags.TypeVariable || + objectType.flags & TypeFlags.TypeVariable && indexType.flags & TypeFlags.Index || isGenericMappedType(objectType)) { - // If either the object type or the index type are type parameters, or if the object type is a mapped - // type with a generic constraint, we are performing a higher-order index access where we cannot - // meaningfully access the properties of the object type. In those cases, we first check that the - // index type is assignable to 'keyof T' for the object type. + // If the object type is a type variable (a type parameter or another indexed access type), if the + // index type is a type variable or an index type, or if the object type is a mapped type with a + // generic constraint, we are performing a higher-order index access where we cannot meaningfully + // access the properties of the object type. In those cases, we first check that the index type is + // assignable to 'keyof T' for the object type. if (accessNode) { if (!isTypeAssignableTo(indexType, getIndexType(objectType))) { error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType)); @@ -6531,19 +6532,19 @@ namespace ts { } function instantiateMappedType(type: MappedType, mapper: TypeMapper): Type { - // Check if we have an isomorphic mapped type, i.e. a type of the form { [P in keyof T]: X } for some - // type parameter T. If so, the mapped type is distributive over a union type and when T is instantiated + // Check if we have a homomorphic mapped type, i.e. a type of the form { [P in keyof T]: X } for some + // type variable T. If so, the mapped type is distributive over a union type and when T is instantiated // to a union type A | B, we produce { [P in keyof A]: X } | { [P in keyof B]: X }. Furthermore, for - // isomorphic mapped types we leave primitive types alone. For example, when T is instantiated to a + // homomorphic mapped types we leave primitive types alone. For example, when T is instantiated to a // union type A | undefined, we produce { [P in keyof A]: X } | undefined. const constraintType = getConstraintTypeFromMappedType(type); if (constraintType.flags & TypeFlags.Index) { - const typeParameter = (constraintType).type; - const mappedTypeParameter = mapper(typeParameter); - if (typeParameter !== mappedTypeParameter) { - return mapType(mappedTypeParameter, t => { + const typeVariable = (constraintType).type; + const mappedTypeVariable = instantiateType(typeVariable, mapper); + if (typeVariable !== mappedTypeVariable) { + return mapType(mappedTypeVariable, t => { if (isMappableType(t)) { - const replacementMapper = createUnaryTypeMapper(typeParameter, t); + const replacementMapper = createUnaryTypeMapper(typeVariable, t); const combinedMapper = mapper.mappedTypes && mapper.mappedTypes.length === 1 ? replacementMapper : combineTypeMappers(replacementMapper, mapper); combinedMapper.mappedTypes = mapper.mappedTypes; return instantiateMappedObjectType(type, combinedMapper); @@ -7274,10 +7275,12 @@ namespace ts { } // Given a type parameter T with a constraint C, a type S is assignable to // keyof T if S is assignable to keyof C. - const constraint = getConstraintOfTypeParameter((target).type); - if (constraint) { - if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) { - return result; + if ((target).type.flags & TypeFlags.TypeParameter) { + const constraint = getConstraintOfTypeParameter((target).type); + if (constraint) { + if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) { + return result; + } } } } @@ -8458,69 +8461,68 @@ namespace ts { // Return true if the given type could possibly reference a type parameter for which // we perform type inference (i.e. a type parameter of a generic function). We cache // results for union and intersection types for performance reasons. - function couldContainTypeParameters(type: Type): boolean { + function couldContainTypeVariables(type: Type): boolean { const objectFlags = getObjectFlags(type); - return !!(type.flags & (TypeFlags.TypeParameter | TypeFlags.IndexedAccess) || - objectFlags & ObjectFlags.Reference && forEach((type).typeArguments, couldContainTypeParameters) || + return !!(type.flags & TypeFlags.TypeVariable || + objectFlags & ObjectFlags.Reference && forEach((type).typeArguments, couldContainTypeVariables) || objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || objectFlags & ObjectFlags.Mapped || - type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeParameters(type)); + type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeVariables(type)); } - function couldUnionOrIntersectionContainTypeParameters(type: UnionOrIntersectionType): boolean { - if (type.couldContainTypeParameters === undefined) { - type.couldContainTypeParameters = forEach(type.types, couldContainTypeParameters); + function couldUnionOrIntersectionContainTypeVariables(type: UnionOrIntersectionType): boolean { + if (type.couldContainTypeVariables === undefined) { + type.couldContainTypeVariables = forEach(type.types, couldContainTypeVariables); } - return type.couldContainTypeParameters; + return type.couldContainTypeVariables; } function isTypeParameterAtTopLevel(type: Type, typeParameter: TypeParameter): boolean { return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((type).types, t => isTypeParameterAtTopLevel(t, typeParameter)); } - // Infer a suitable input type for an isomorphic mapped type { [P in keyof T]: X }. We construct + // Infer a suitable input type for a homomorphic mapped type { [P in keyof T]: X }. We construct // an object type with the same set of properties as the source type, where the type of each - // property is computed by inferring from the source property type to X for a synthetic type - // parameter T[P] (i.e. we treat the type T[P] as the type parameter we're inferring for). - function inferTypeForIsomorphicMappedType(source: Type, target: MappedType): Type { - if (!isMappableType(source)) { - return source; - } - const typeParameter = getIndexedAccessType((getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target)); - const typeParameterArray = [typeParameter]; + // property is computed by inferring from the source property type to X for the type + // variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for). + function inferTypeForHomomorphicMappedType(source: Type, target: MappedType): Type { + const properties = getPropertiesOfType(source); + let indexInfo = getIndexInfoOfType(source, IndexKind.String); + if (properties.length === 0 && !indexInfo) { + return undefined; + } + const typeVariable = getIndexedAccessType((getConstraintTypeFromMappedType(target)).type, getTypeParameterFromMappedType(target)); + const typeVariableArray = [typeVariable]; const typeInferences = createTypeInferencesObject(); const typeInferencesArray = [typeInferences]; const templateType = getTemplateTypeFromMappedType(target); const readonlyMask = target.declaration.readonlyToken ? false : true; const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional; - const properties = getPropertiesOfType(source); const members = createSymbolTable(properties); - let hasInferredTypes = false; for (const prop of properties) { const inferredPropType = inferTargetType(getTypeOfSymbol(prop)); - if (inferredPropType) { - const inferredProp = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | prop.flags & optionalMask, prop.name); - inferredProp.declarations = prop.declarations; - inferredProp.type = inferredPropType; - inferredProp.isReadonly = readonlyMask && isReadonlySymbol(prop); - members[prop.name] = inferredProp; - hasInferredTypes = true; + if (!inferredPropType) { + return undefined; } + const inferredProp = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | prop.flags & optionalMask, prop.name); + inferredProp.declarations = prop.declarations; + inferredProp.type = inferredPropType; + inferredProp.isReadonly = readonlyMask && isReadonlySymbol(prop); + members[prop.name] = inferredProp; } - let indexInfo = getIndexInfoOfType(source, IndexKind.String); if (indexInfo) { const inferredIndexType = inferTargetType(indexInfo.type); - if (inferredIndexType) { - indexInfo = createIndexInfo(inferredIndexType, readonlyMask && indexInfo.isReadonly); - hasInferredTypes = true; + if (!inferredIndexType) { + return undefined; } + indexInfo = createIndexInfo(inferredIndexType, readonlyMask && indexInfo.isReadonly); } - return hasInferredTypes ? createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined) : source; + return createAnonymousType(undefined, members, emptyArray, emptyArray, indexInfo, undefined); function inferTargetType(sourceType: Type): Type { typeInferences.primary = undefined; typeInferences.secondary = undefined; - inferTypes(typeParameterArray, typeInferencesArray, sourceType, templateType); + inferTypes(typeVariableArray, typeInferencesArray, sourceType, templateType); const inferences = typeInferences.primary || typeInferences.secondary; return inferences && getUnionType(inferences, /*subtypeReduction*/ true); } @@ -8530,7 +8532,7 @@ namespace ts { inferTypes(context.signature.typeParameters, context.inferences, originalSource, originalTarget); } - function inferTypes(typeParameters: Type[], typeInferences: TypeInferences[], originalSource: Type, originalTarget: Type) { + function inferTypes(typeVariables: TypeVariable[], typeInferences: TypeInferences[], originalSource: Type, originalTarget: Type) { let sourceStack: Type[]; let targetStack: Type[]; let depth = 0; @@ -8548,7 +8550,7 @@ namespace ts { } function inferFromTypes(source: Type, target: Type) { - if (!couldContainTypeParameters(target)) { + if (!couldContainTypeVariables(target)) { return; } if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) { @@ -8598,7 +8600,7 @@ namespace ts { target = removeTypesFromUnionOrIntersection(target, matchingTypes); } } - if (target.flags & (TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) { + if (target.flags & TypeFlags.TypeVariable) { // If target is a type parameter, make an inference, unless the source type contains // the anyFunctionType (the wildcard type that's used to avoid contextually typing functions). // Because the anyFunctionType is internal, it should not be exposed to the user by adding @@ -8608,8 +8610,8 @@ namespace ts { if (source.flags & TypeFlags.ContainsAnyFunctionType) { return; } - for (let i = 0; i < typeParameters.length; i++) { - if (target === typeParameters[i]) { + for (let i = 0; i < typeVariables.length; i++) { + if (target === typeVariables[i]) { const inferences = typeInferences[i]; if (!inferences.isFixed) { // Any inferences that are made to a type parameter in a union type are inferior @@ -8643,24 +8645,24 @@ namespace ts { } else if (target.flags & TypeFlags.UnionOrIntersection) { const targetTypes = (target).types; - let typeParameterCount = 0; - let typeParameter: TypeParameter; - // First infer to each type in union or intersection that isn't a type parameter + let typeVariableCount = 0; + let typeVariable: TypeVariable; + // First infer to each type in union or intersection that isn't a type variable for (const t of targetTypes) { - if (t.flags & TypeFlags.TypeParameter && contains(typeParameters, t)) { - typeParameter = t; - typeParameterCount++; + if (t.flags & TypeFlags.TypeVariable && contains(typeVariables, t)) { + typeVariable = t; + typeVariableCount++; } else { inferFromTypes(source, t); } } - // Next, if target containings a single naked type parameter, make a secondary inference to that type - // parameter. This gives meaningful results for union types in co-variant positions and intersection + // Next, if target containings a single naked type variable, make a secondary inference to that type + // variable. This gives meaningful results for union types in co-variant positions and intersection // types in contra-variant positions (such as callback parameters). - if (typeParameterCount === 1) { + if (typeVariableCount === 1) { inferiority++; - inferFromTypes(source, typeParameter); + inferFromTypes(source, typeVariable); inferiority--; } } @@ -8702,12 +8704,15 @@ namespace ts { if (getObjectFlags(target) & ObjectFlags.Mapped) { const constraintType = getConstraintTypeFromMappedType(target); if (constraintType.flags & TypeFlags.Index) { - // We're inferring from some source type S to an isomorphic mapped type { [P in keyof T]: X }, - // where T is a type parameter. Use inferTypeForIsomorphicMappedType to infer a suitable source + // We're inferring from some source type S to a homomorphic mapped type { [P in keyof T]: X }, + // where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source // type and then infer from that type to T. - const index = indexOf(typeParameters, (constraintType).type); + const index = indexOf(typeVariables, (constraintType).type); if (index >= 0 && !typeInferences[index].isFixed) { - inferFromTypes(inferTypeForIsomorphicMappedType(source, target), typeParameters[index]); + const inferredType = inferTypeForHomomorphicMappedType(source, target); + if (inferredType) { + inferFromTypes(inferredType, typeVariables[index]); + } } return; } @@ -14388,7 +14393,7 @@ namespace ts { if (!(isTypeComparableTo(leftType, stringType) || isTypeOfKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) { error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); } - if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) { + if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeVariable)) { error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter); } return booleanType; @@ -17331,7 +17336,7 @@ namespace ts { // unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved // in this case error about missing name is already reported - do not report extra one - if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeParameter | TypeFlags.IndexedAccess)) { + if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeVariable)) { error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bc400df257d9e..aa96b35d758cc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2797,6 +2797,7 @@ namespace ts { UnionOrIntersection = Union | Intersection, StructuredType = Object | Union | Intersection, StructuredOrTypeParameter = StructuredType | TypeParameter | Index, + TypeVariable = TypeParameter | IndexedAccess, // 'Narrowable' types are types where narrowing actually narrows. // This *should* be every type other than null, undefined, void, and never @@ -2907,7 +2908,7 @@ namespace ts { /* @internal */ resolvedProperties: SymbolTable; // Cache of resolved properties /* @internal */ - couldContainTypeParameters: boolean; + couldContainTypeVariables: boolean; } export interface UnionType extends UnionOrIntersectionType { } @@ -2963,8 +2964,13 @@ namespace ts { iteratorElementType?: Type; } + export interface TypeVariable extends Type { + /* @internal */ + resolvedIndexType: IndexType; + } + // Type parameters (TypeFlags.TypeParameter) - export interface TypeParameter extends Type { + export interface TypeParameter extends TypeVariable { constraint: Type; // Constraint /* @internal */ target?: TypeParameter; // Instantiation target @@ -2973,20 +2979,21 @@ namespace ts { /* @internal */ resolvedApparentType: Type; /* @internal */ - resolvedIndexType: IndexType; - /* @internal */ isThisType?: boolean; } - export interface IndexType extends Type { - type: TypeParameter; - } - - export interface IndexedAccessType extends Type { + // Indexed access types (TypeFlags.IndexedAccess) + // Possible forms are T[xxx], xxx[T], or xxx[keyof T], where T is a type variable + export interface IndexedAccessType extends TypeVariable { objectType: Type; indexType: Type; } + // keyof T types (TypeFlags.Index) + export interface IndexType extends Type { + type: TypeVariable; + } + export const enum SignatureKind { Call, Construct, From b3b23597a8362c1501b810ecd67048a39ee7794d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 2 Dec 2016 14:29:23 -0800 Subject: [PATCH 05/17] Accept new baselines --- tests/baselines/reference/keyofAndIndexedAccess.types | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index 587100496e9ee..fa54c614f07a6 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -826,7 +826,7 @@ function f54(obj: T, key: keyof T) { >T : T for (let s in obj[key]) { ->s : string +>s : keyof T[keyof T] >obj[key] : T[keyof T] >obj : T >key : keyof T @@ -851,7 +851,7 @@ function f55(obj: T, key: K) { >K : K for (let s in obj[key]) { ->s : string +>s : keyof T[K] >obj[key] : T[K] >obj : T >key : K From 1e2425ebfce32a5207578214bf15c5591778a0a7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 2 Dec 2016 14:50:14 -0800 Subject: [PATCH 06/17] Add tests --- .../types/keyof/keyofAndIndexedAccess.ts | 42 +++++++++++++++++++ .../mapped/isomorphicMappedTypeInference.ts | 27 +++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts index 9aa057986078e..3cad745402f95 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts @@ -250,3 +250,45 @@ class OtherPerson { return getProperty(this, "parts") } } + +// Modified repro from #12544 + +function path(obj: T, key1: K1): T[K1]; +function path(obj: T, key1: K1, key2: K2): T[K1][K2]; +function path(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; +function path(obj: any, ...keys: (string | number)[]): any; +function path(obj: any, ...keys: (string | number)[]): any { + let result = obj; + for (let k of keys) { + result = result[k]; + } + return result; +} + +type Thing = { + a: { x: number, y: string }, + b: boolean +}; + + +function f1(thing: Thing) { + let x1 = path(thing, 'a'); // { x: number, y: string } + let x2 = path(thing, 'a', 'y'); // string + let x3 = path(thing, 'b'); // boolean + let x4 = path(thing, ...['a', 'x']); // any +} + +// Repro from comment in #12114 + +const assignTo2 = (object: T, key1: K1, key2: K2) => + (value: T[K1][K2]) => object[key1][key2] = value; + +// Modified repro from #12573 + +declare function one(handler: (t: T) => void): T +var empty = one(() => {}) // inferred as {}, expected + +type Handlers = { [K in keyof T]: (t: T[K]) => void } +declare function on(handlerHash: Handlers): T +var hashOfEmpty1 = on({ test: () => {} }); // {} +var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } \ No newline at end of file diff --git a/tests/cases/conformance/types/mapped/isomorphicMappedTypeInference.ts b/tests/cases/conformance/types/mapped/isomorphicMappedTypeInference.ts index 4aab2d95ba39b..330d99dd7f152 100644 --- a/tests/cases/conformance/types/mapped/isomorphicMappedTypeInference.ts +++ b/tests/cases/conformance/types/mapped/isomorphicMappedTypeInference.ts @@ -120,4 +120,29 @@ function f10(foo: Foo) { let x = validate(foo); // { a: number, readonly b: string } let y = clone(foo); // { a?: number, b: string } let z = validateAndClone(foo); // { a: number, b: string } -} \ No newline at end of file +} + +// Repro from #12606 + +type Func = (...args: any[]) => T; +type Spec = { + [P in keyof T]: Func | Spec ; +}; + +/** + * Given a spec object recursively mapping properties to functions, creates a function + * producing an object of the same structure, by mapping each property to the result + * of calling its associated function with the supplied arguments. + */ +declare function applySpec(obj: Spec): (...args: any[]) => T; + +// Infers g1: (...args: any[]) => { sum: number, nested: { mul: string } } +var g1 = applySpec({ + sum: (a: any) => 3, + nested: { + mul: (b: any) => "n" + } +}); + +// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } } +var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); \ No newline at end of file From fe0b66a00c5e11cd593ab99c99e8ab4262973e80 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 2 Dec 2016 14:50:21 -0800 Subject: [PATCH 07/17] Accept new baselines --- .../isomorphicMappedTypeInference.js | 59 ++++- .../isomorphicMappedTypeInference.symbols | 66 ++++++ .../isomorphicMappedTypeInference.types | 79 +++++++ .../reference/keyofAndIndexedAccess.js | 91 +++++++- .../reference/keyofAndIndexedAccess.symbols | 193 +++++++++++++++ .../reference/keyofAndIndexedAccess.types | 220 ++++++++++++++++++ 6 files changed, 706 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/isomorphicMappedTypeInference.js b/tests/baselines/reference/isomorphicMappedTypeInference.js index 87f3e1c40173d..0ed2285ed04ba 100644 --- a/tests/baselines/reference/isomorphicMappedTypeInference.js +++ b/tests/baselines/reference/isomorphicMappedTypeInference.js @@ -118,7 +118,32 @@ function f10(foo: Foo) { let x = validate(foo); // { a: number, readonly b: string } let y = clone(foo); // { a?: number, b: string } let z = validateAndClone(foo); // { a: number, b: string } -} +} + +// Repro from #12606 + +type Func = (...args: any[]) => T; +type Spec = { + [P in keyof T]: Func | Spec ; +}; + +/** + * Given a spec object recursively mapping properties to functions, creates a function + * producing an object of the same structure, by mapping each property to the result + * of calling its associated function with the supplied arguments. + */ +declare function applySpec(obj: Spec): (...args: any[]) => T; + +// Infers g1: (...args: any[]) => { sum: number, nested: { mul: string } } +var g1 = applySpec({ + sum: (a: any) => 3, + nested: { + mul: (b: any) => "n" + } +}); + +// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } } +var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); //// [isomorphicMappedTypeInference.js] function box(x) { @@ -210,6 +235,15 @@ function f10(foo) { var y = clone(foo); // { a?: number, b: string } var z = validateAndClone(foo); // { a: number, b: string } } +// Infers g1: (...args: any[]) => { sum: number, nested: { mul: string } } +var g1 = applySpec({ + sum: function (a) { return 3; }, + nested: { + mul: function (b) { return "n"; } + } +}); +// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } } +var g2 = applySpec({ foo: { bar: { baz: function (x) { return true; } } } }); //// [isomorphicMappedTypeInference.d.ts] @@ -254,3 +288,26 @@ declare type Foo = { readonly b: string; }; declare function f10(foo: Foo): void; +declare type Func = (...args: any[]) => T; +declare type Spec = { + [P in keyof T]: Func | Spec; +}; +/** + * Given a spec object recursively mapping properties to functions, creates a function + * producing an object of the same structure, by mapping each property to the result + * of calling its associated function with the supplied arguments. + */ +declare function applySpec(obj: Spec): (...args: any[]) => T; +declare var g1: (...args: any[]) => { + sum: number; + nested: { + mul: string; + }; +}; +declare var g2: (...args: any[]) => { + foo: { + bar: { + baz: boolean; + }; + }; +}; diff --git a/tests/baselines/reference/isomorphicMappedTypeInference.symbols b/tests/baselines/reference/isomorphicMappedTypeInference.symbols index 3e8a92e769109..92bcbb697ec53 100644 --- a/tests/baselines/reference/isomorphicMappedTypeInference.symbols +++ b/tests/baselines/reference/isomorphicMappedTypeInference.symbols @@ -393,3 +393,69 @@ function f10(foo: Foo) { >validateAndClone : Symbol(validateAndClone, Decl(isomorphicMappedTypeInference.ts, 107, 69)) >foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 115, 13)) } + +// Repro from #12606 + +type Func = (...args: any[]) => T; +>Func : Symbol(Func, Decl(isomorphicMappedTypeInference.ts, 119, 1)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 123, 10)) +>args : Symbol(args, Decl(isomorphicMappedTypeInference.ts, 123, 16)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 123, 10)) + +type Spec = { +>Spec : Symbol(Spec, Decl(isomorphicMappedTypeInference.ts, 123, 37)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 124, 10)) + + [P in keyof T]: Func | Spec ; +>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 125, 5)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 124, 10)) +>Func : Symbol(Func, Decl(isomorphicMappedTypeInference.ts, 119, 1)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 124, 10)) +>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 125, 5)) +>Spec : Symbol(Spec, Decl(isomorphicMappedTypeInference.ts, 123, 37)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 124, 10)) +>P : Symbol(P, Decl(isomorphicMappedTypeInference.ts, 125, 5)) + +}; + +/** + * Given a spec object recursively mapping properties to functions, creates a function + * producing an object of the same structure, by mapping each property to the result + * of calling its associated function with the supplied arguments. + */ +declare function applySpec(obj: Spec): (...args: any[]) => T; +>applySpec : Symbol(applySpec, Decl(isomorphicMappedTypeInference.ts, 126, 2)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 133, 27)) +>obj : Symbol(obj, Decl(isomorphicMappedTypeInference.ts, 133, 30)) +>Spec : Symbol(Spec, Decl(isomorphicMappedTypeInference.ts, 123, 37)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 133, 27)) +>args : Symbol(args, Decl(isomorphicMappedTypeInference.ts, 133, 46)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 133, 27)) + +// Infers g1: (...args: any[]) => { sum: number, nested: { mul: string } } +var g1 = applySpec({ +>g1 : Symbol(g1, Decl(isomorphicMappedTypeInference.ts, 136, 3)) +>applySpec : Symbol(applySpec, Decl(isomorphicMappedTypeInference.ts, 126, 2)) + + sum: (a: any) => 3, +>sum : Symbol(sum, Decl(isomorphicMappedTypeInference.ts, 136, 20)) +>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 137, 10)) + + nested: { +>nested : Symbol(nested, Decl(isomorphicMappedTypeInference.ts, 137, 23)) + + mul: (b: any) => "n" +>mul : Symbol(mul, Decl(isomorphicMappedTypeInference.ts, 138, 13)) +>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 139, 14)) + } +}); + +// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } } +var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); +>g2 : Symbol(g2, Decl(isomorphicMappedTypeInference.ts, 144, 3)) +>applySpec : Symbol(applySpec, Decl(isomorphicMappedTypeInference.ts, 126, 2)) +>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 144, 20)) +>bar : Symbol(bar, Decl(isomorphicMappedTypeInference.ts, 144, 27)) +>baz : Symbol(baz, Decl(isomorphicMappedTypeInference.ts, 144, 34)) +>x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 144, 41)) + diff --git a/tests/baselines/reference/isomorphicMappedTypeInference.types b/tests/baselines/reference/isomorphicMappedTypeInference.types index eee2d6cbe7d40..b4d1383071bb7 100644 --- a/tests/baselines/reference/isomorphicMappedTypeInference.types +++ b/tests/baselines/reference/isomorphicMappedTypeInference.types @@ -467,3 +467,82 @@ function f10(foo: Foo) { >validateAndClone : (obj: { readonly [P in keyof T]?: T[P] | undefined; }) => T >foo : Foo } + +// Repro from #12606 + +type Func = (...args: any[]) => T; +>Func : Func +>T : T +>args : any[] +>T : T + +type Spec = { +>Spec : Spec +>T : T + + [P in keyof T]: Func | Spec ; +>P : P +>T : T +>Func : Func +>T : T +>P : P +>Spec : Spec +>T : T +>P : P + +}; + +/** + * Given a spec object recursively mapping properties to functions, creates a function + * producing an object of the same structure, by mapping each property to the result + * of calling its associated function with the supplied arguments. + */ +declare function applySpec(obj: Spec): (...args: any[]) => T; +>applySpec : (obj: Spec) => (...args: any[]) => T +>T : T +>obj : Spec +>Spec : Spec +>T : T +>args : any[] +>T : T + +// Infers g1: (...args: any[]) => { sum: number, nested: { mul: string } } +var g1 = applySpec({ +>g1 : (...args: any[]) => { sum: number; nested: { mul: string; }; } +>applySpec({ sum: (a: any) => 3, nested: { mul: (b: any) => "n" }}) : (...args: any[]) => { sum: number; nested: { mul: string; }; } +>applySpec : (obj: Spec) => (...args: any[]) => T +>{ sum: (a: any) => 3, nested: { mul: (b: any) => "n" }} : { sum: (a: any) => number; nested: { mul: (b: any) => string; }; } + + sum: (a: any) => 3, +>sum : (a: any) => number +>(a: any) => 3 : (a: any) => number +>a : any +>3 : 3 + + nested: { +>nested : { mul: (b: any) => string; } +>{ mul: (b: any) => "n" } : { mul: (b: any) => string; } + + mul: (b: any) => "n" +>mul : (b: any) => string +>(b: any) => "n" : (b: any) => string +>b : any +>"n" : "n" + } +}); + +// Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } } +var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); +>g2 : (...args: any[]) => { foo: { bar: { baz: boolean; }; }; } +>applySpec({ foo: { bar: { baz: (x: any) => true } } }) : (...args: any[]) => { foo: { bar: { baz: boolean; }; }; } +>applySpec : (obj: Spec) => (...args: any[]) => T +>{ foo: { bar: { baz: (x: any) => true } } } : { foo: { bar: { baz: (x: any) => boolean; }; }; } +>foo : { bar: { baz: (x: any) => boolean; }; } +>{ bar: { baz: (x: any) => true } } : { bar: { baz: (x: any) => boolean; }; } +>bar : { baz: (x: any) => boolean; } +>{ baz: (x: any) => true } : { baz: (x: any) => boolean; } +>baz : (x: any) => boolean +>(x: any) => true : (x: any) => boolean +>x : any +>true : true + diff --git a/tests/baselines/reference/keyofAndIndexedAccess.js b/tests/baselines/reference/keyofAndIndexedAccess.js index 8d8f125f39e55..0e630be9a4392 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.js +++ b/tests/baselines/reference/keyofAndIndexedAccess.js @@ -250,7 +250,48 @@ class OtherPerson { return getProperty(this, "parts") } } - + +// Modified repro from #12544 + +function path(obj: T, key1: K1): T[K1]; +function path(obj: T, key1: K1, key2: K2): T[K1][K2]; +function path(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; +function path(obj: any, ...keys: (string | number)[]): any; +function path(obj: any, ...keys: (string | number)[]): any { + let result = obj; + for (let k of keys) { + result = result[k]; + } + return result; +} + +type Thing = { + a: { x: number, y: string }, + b: boolean +}; + + +function f1(thing: Thing) { + let x1 = path(thing, 'a'); // { x: number, y: string } + let x2 = path(thing, 'a', 'y'); // string + let x3 = path(thing, 'b'); // boolean + let x4 = path(thing, ...['a', 'x']); // any +} + +// Repro from comment in #12114 + +const assignTo2 = (object: T, key1: K1, key2: K2) => + (value: T[K1][K2]) => object[key1][key2] = value; + +// Modified repro from #12573 + +declare function one(handler: (t: T) => void): T +var empty = one(() => {}) // inferred as {}, expected + +type Handlers = { [K in keyof T]: (t: T[K]) => void } +declare function on(handlerHash: Handlers): T +var hashOfEmpty1 = on({ test: () => {} }); // {} +var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } //// [keyofAndIndexedAccess.js] var __extends = (this && this.__extends) || function (d, b) { @@ -430,6 +471,31 @@ var OtherPerson = (function () { }; return OtherPerson; }()); +function path(obj) { + var keys = []; + for (var _i = 1; _i < arguments.length; _i++) { + keys[_i - 1] = arguments[_i]; + } + var result = obj; + for (var _a = 0, keys_1 = keys; _a < keys_1.length; _a++) { + var k = keys_1[_a]; + result = result[k]; + } + return result; +} +function f1(thing) { + var x1 = path(thing, 'a'); // { x: number, y: string } + var x2 = path(thing, 'a', 'y'); // string + var x3 = path(thing, 'b'); // boolean + var x4 = path.apply(void 0, [thing].concat(['a', 'x'])); // any +} +// Repro from comment in #12114 +var assignTo2 = function (object, key1, key2) { + return function (value) { return object[key1][key2] = value; }; +}; +var empty = one(function () { }); // inferred as {}, expected +var hashOfEmpty1 = on({ test: function () { } }); // {} +var hashOfEmpty2 = on({ test: function (x) { } }); // { test: boolean } //// [keyofAndIndexedAccess.d.ts] @@ -551,3 +617,26 @@ declare class OtherPerson { constructor(parts: number); getParts(): number; } +declare function path(obj: T, key1: K1): T[K1]; +declare function path(obj: T, key1: K1, key2: K2): T[K1][K2]; +declare function path(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; +declare function path(obj: any, ...keys: (string | number)[]): any; +declare type Thing = { + a: { + x: number; + y: string; + }; + b: boolean; +}; +declare function f1(thing: Thing): void; +declare const assignTo2: (object: T, key1: K1, key2: K2) => (value: T[K1][K2]) => T[K1][K2]; +declare function one(handler: (t: T) => void): T; +declare var empty: {}; +declare type Handlers = { + [K in keyof T]: (t: T[K]) => void; +}; +declare function on(handlerHash: Handlers): T; +declare var hashOfEmpty1: {}; +declare var hashOfEmpty2: { + test: boolean; +}; diff --git a/tests/baselines/reference/keyofAndIndexedAccess.symbols b/tests/baselines/reference/keyofAndIndexedAccess.symbols index 2371bf419c854..e67db316dad7c 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccess.symbols @@ -848,3 +848,196 @@ class OtherPerson { } } +// Modified repro from #12544 + +function path(obj: T, key1: K1): T[K1]; +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 254, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 254, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 254, 14)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 254, 37)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 254, 14)) +>key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 254, 44)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 254, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 254, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 254, 16)) + +function path(obj: T, key1: K1, key2: K2): T[K1][K2]; +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 255, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 255, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 255, 14)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 255, 36)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 255, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 255, 16)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 255, 61)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 255, 14)) +>key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 255, 68)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 255, 16)) +>key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 255, 78)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 255, 36)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 255, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 255, 16)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 255, 36)) + +function path(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 256, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 256, 36)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 256, 16)) +>K3 : Symbol(K3, Decl(keyofAndIndexedAccess.ts, 256, 60)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 256, 16)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 256, 36)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 256, 89)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) +>key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 256, 96)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 256, 16)) +>key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 256, 106)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 256, 36)) +>key3 : Symbol(key3, Decl(keyofAndIndexedAccess.ts, 256, 116)) +>K3 : Symbol(K3, Decl(keyofAndIndexedAccess.ts, 256, 60)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 256, 16)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 256, 36)) +>K3 : Symbol(K3, Decl(keyofAndIndexedAccess.ts, 256, 60)) + +function path(obj: any, ...keys: (string | number)[]): any; +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 257, 14)) +>keys : Symbol(keys, Decl(keyofAndIndexedAccess.ts, 257, 23)) + +function path(obj: any, ...keys: (string | number)[]): any { +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 258, 14)) +>keys : Symbol(keys, Decl(keyofAndIndexedAccess.ts, 258, 23)) + + let result = obj; +>result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 259, 7)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 258, 14)) + + for (let k of keys) { +>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 260, 12)) +>keys : Symbol(keys, Decl(keyofAndIndexedAccess.ts, 258, 23)) + + result = result[k]; +>result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 259, 7)) +>result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 259, 7)) +>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 260, 12)) + } + return result; +>result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 259, 7)) +} + +type Thing = { +>Thing : Symbol(Thing, Decl(keyofAndIndexedAccess.ts, 264, 1)) + + a: { x: number, y: string }, +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 266, 14)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 267, 8)) +>y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 267, 19)) + + b: boolean +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 267, 32)) + +}; + + +function f1(thing: Thing) { +>f1 : Symbol(f1, Decl(keyofAndIndexedAccess.ts, 269, 2)) +>thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 272, 12)) +>Thing : Symbol(Thing, Decl(keyofAndIndexedAccess.ts, 264, 1)) + + let x1 = path(thing, 'a'); // { x: number, y: string } +>x1 : Symbol(x1, Decl(keyofAndIndexedAccess.ts, 273, 7)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) +>thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 272, 12)) + + let x2 = path(thing, 'a', 'y'); // string +>x2 : Symbol(x2, Decl(keyofAndIndexedAccess.ts, 274, 7)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) +>thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 272, 12)) + + let x3 = path(thing, 'b'); // boolean +>x3 : Symbol(x3, Decl(keyofAndIndexedAccess.ts, 275, 7)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) +>thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 272, 12)) + + let x4 = path(thing, ...['a', 'x']); // any +>x4 : Symbol(x4, Decl(keyofAndIndexedAccess.ts, 276, 7)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) +>thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 272, 12)) +} + +// Repro from comment in #12114 + +const assignTo2 = (object: T, key1: K1, key2: K2) => +>assignTo2 : Symbol(assignTo2, Decl(keyofAndIndexedAccess.ts, 281, 5)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 281, 19)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 281, 21)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 281, 19)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 281, 41)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 281, 19)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 281, 21)) +>object : Symbol(object, Decl(keyofAndIndexedAccess.ts, 281, 66)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 281, 19)) +>key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 281, 76)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 281, 21)) +>key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 281, 86)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 281, 41)) + + (value: T[K1][K2]) => object[key1][key2] = value; +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 282, 5)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 281, 19)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 281, 21)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 281, 41)) +>object : Symbol(object, Decl(keyofAndIndexedAccess.ts, 281, 66)) +>key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 281, 76)) +>key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 281, 86)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 282, 5)) + +// Modified repro from #12573 + +declare function one(handler: (t: T) => void): T +>one : Symbol(one, Decl(keyofAndIndexedAccess.ts, 282, 53)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 21)) +>handler : Symbol(handler, Decl(keyofAndIndexedAccess.ts, 286, 24)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 286, 34)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 21)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 21)) + +var empty = one(() => {}) // inferred as {}, expected +>empty : Symbol(empty, Decl(keyofAndIndexedAccess.ts, 287, 3)) +>one : Symbol(one, Decl(keyofAndIndexedAccess.ts, 282, 53)) + +type Handlers = { [K in keyof T]: (t: T[K]) => void } +>Handlers : Symbol(Handlers, Decl(keyofAndIndexedAccess.ts, 287, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 289, 14)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 289, 22)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 289, 14)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 289, 38)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 289, 14)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 289, 22)) + +declare function on(handlerHash: Handlers): T +>on : Symbol(on, Decl(keyofAndIndexedAccess.ts, 289, 56)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 290, 20)) +>handlerHash : Symbol(handlerHash, Decl(keyofAndIndexedAccess.ts, 290, 23)) +>Handlers : Symbol(Handlers, Decl(keyofAndIndexedAccess.ts, 287, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 290, 20)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 290, 20)) + +var hashOfEmpty1 = on({ test: () => {} }); // {} +>hashOfEmpty1 : Symbol(hashOfEmpty1, Decl(keyofAndIndexedAccess.ts, 291, 3)) +>on : Symbol(on, Decl(keyofAndIndexedAccess.ts, 289, 56)) +>test : Symbol(test, Decl(keyofAndIndexedAccess.ts, 291, 23)) + +var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } +>hashOfEmpty2 : Symbol(hashOfEmpty2, Decl(keyofAndIndexedAccess.ts, 292, 3)) +>on : Symbol(on, Decl(keyofAndIndexedAccess.ts, 289, 56)) +>test : Symbol(test, Decl(keyofAndIndexedAccess.ts, 292, 23)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 292, 31)) + diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index fa54c614f07a6..9e1e7d4b4d171 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -982,3 +982,223 @@ class OtherPerson { } } +// Modified repro from #12544 + +function path(obj: T, key1: K1): T[K1]; +>path : { (obj: T, key1: K1): T[K1]; (obj: T, key1: K1, key2: K2): T[K1][K2]; (obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; (obj: any, ...keys: (string | number)[]): any; } +>T : T +>K1 : K1 +>T : T +>obj : T +>T : T +>key1 : K1 +>K1 : K1 +>T : T +>K1 : K1 + +function path(obj: T, key1: K1, key2: K2): T[K1][K2]; +>path : { (obj: T, key1: K1): T[K1]; (obj: T, key1: K1, key2: K2): T[K1][K2]; (obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; (obj: any, ...keys: (string | number)[]): any; } +>T : T +>K1 : K1 +>T : T +>K2 : K2 +>T : T +>K1 : K1 +>obj : T +>T : T +>key1 : K1 +>K1 : K1 +>key2 : K2 +>K2 : K2 +>T : T +>K1 : K1 +>K2 : K2 + +function path(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; +>path : { (obj: T, key1: K1): T[K1]; (obj: T, key1: K1, key2: K2): T[K1][K2]; (obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; (obj: any, ...keys: (string | number)[]): any; } +>T : T +>K1 : K1 +>T : T +>K2 : K2 +>T : T +>K1 : K1 +>K3 : K3 +>T : T +>K1 : K1 +>K2 : K2 +>obj : T +>T : T +>key1 : K1 +>K1 : K1 +>key2 : K2 +>K2 : K2 +>key3 : K3 +>K3 : K3 +>T : T +>K1 : K1 +>K2 : K2 +>K3 : K3 + +function path(obj: any, ...keys: (string | number)[]): any; +>path : { (obj: T, key1: K1): T[K1]; (obj: T, key1: K1, key2: K2): T[K1][K2]; (obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; (obj: any, ...keys: (string | number)[]): any; } +>obj : any +>keys : (string | number)[] + +function path(obj: any, ...keys: (string | number)[]): any { +>path : { (obj: T, key1: K1): T[K1]; (obj: T, key1: K1, key2: K2): T[K1][K2]; (obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; (obj: any, ...keys: (string | number)[]): any; } +>obj : any +>keys : (string | number)[] + + let result = obj; +>result : any +>obj : any + + for (let k of keys) { +>k : string | number +>keys : (string | number)[] + + result = result[k]; +>result = result[k] : any +>result : any +>result[k] : any +>result : any +>k : string | number + } + return result; +>result : any +} + +type Thing = { +>Thing : Thing + + a: { x: number, y: string }, +>a : { x: number; y: string; } +>x : number +>y : string + + b: boolean +>b : boolean + +}; + + +function f1(thing: Thing) { +>f1 : (thing: Thing) => void +>thing : Thing +>Thing : Thing + + let x1 = path(thing, 'a'); // { x: number, y: string } +>x1 : { x: number; y: string; } +>path(thing, 'a') : { x: number; y: string; } +>path : { (obj: T, key1: K1): T[K1]; (obj: T, key1: K1, key2: K2): T[K1][K2]; (obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; (obj: any, ...keys: (string | number)[]): any; } +>thing : Thing +>'a' : "a" + + let x2 = path(thing, 'a', 'y'); // string +>x2 : string +>path(thing, 'a', 'y') : string +>path : { (obj: T, key1: K1): T[K1]; (obj: T, key1: K1, key2: K2): T[K1][K2]; (obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; (obj: any, ...keys: (string | number)[]): any; } +>thing : Thing +>'a' : "a" +>'y' : "y" + + let x3 = path(thing, 'b'); // boolean +>x3 : boolean +>path(thing, 'b') : boolean +>path : { (obj: T, key1: K1): T[K1]; (obj: T, key1: K1, key2: K2): T[K1][K2]; (obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; (obj: any, ...keys: (string | number)[]): any; } +>thing : Thing +>'b' : "b" + + let x4 = path(thing, ...['a', 'x']); // any +>x4 : any +>path(thing, ...['a', 'x']) : any +>path : { (obj: T, key1: K1): T[K1]; (obj: T, key1: K1, key2: K2): T[K1][K2]; (obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; (obj: any, ...keys: (string | number)[]): any; } +>thing : Thing +>...['a', 'x'] : string +>['a', 'x'] : string[] +>'a' : "a" +>'x' : "x" +} + +// Repro from comment in #12114 + +const assignTo2 = (object: T, key1: K1, key2: K2) => +>assignTo2 : (object: T, key1: K1, key2: K2) => (value: T[K1][K2]) => T[K1][K2] +>(object: T, key1: K1, key2: K2) => (value: T[K1][K2]) => object[key1][key2] = value : (object: T, key1: K1, key2: K2) => (value: T[K1][K2]) => T[K1][K2] +>T : T +>K1 : K1 +>T : T +>K2 : K2 +>T : T +>K1 : K1 +>object : T +>T : T +>key1 : K1 +>K1 : K1 +>key2 : K2 +>K2 : K2 + + (value: T[K1][K2]) => object[key1][key2] = value; +>(value: T[K1][K2]) => object[key1][key2] = value : (value: T[K1][K2]) => T[K1][K2] +>value : T[K1][K2] +>T : T +>K1 : K1 +>K2 : K2 +>object[key1][key2] = value : T[K1][K2] +>object[key1][key2] : T[K1][K2] +>object[key1] : T[K1] +>object : T +>key1 : K1 +>key2 : K2 +>value : T[K1][K2] + +// Modified repro from #12573 + +declare function one(handler: (t: T) => void): T +>one : (handler: (t: T) => void) => T +>T : T +>handler : (t: T) => void +>t : T +>T : T +>T : T + +var empty = one(() => {}) // inferred as {}, expected +>empty : {} +>one(() => {}) : {} +>one : (handler: (t: T) => void) => T +>() => {} : () => void + +type Handlers = { [K in keyof T]: (t: T[K]) => void } +>Handlers : Handlers +>T : T +>K : K +>T : T +>t : T[K] +>T : T +>K : K + +declare function on(handlerHash: Handlers): T +>on : (handlerHash: Handlers) => T +>T : T +>handlerHash : Handlers +>Handlers : Handlers +>T : T +>T : T + +var hashOfEmpty1 = on({ test: () => {} }); // {} +>hashOfEmpty1 : {} +>on({ test: () => {} }) : {} +>on : (handlerHash: Handlers) => T +>{ test: () => {} } : { test: () => void; } +>test : () => void +>() => {} : () => void + +var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } +>hashOfEmpty2 : { test: boolean; } +>on({ test: (x: boolean) => {} }) : { test: boolean; } +>on : (handlerHash: Handlers) => T +>{ test: (x: boolean) => {} } : { test: (x: boolean) => void; } +>test : (x: boolean) => void +>(x: boolean) => {} : (x: boolean) => void +>x : boolean + From 931006338a2cfd6c8adea893dd5732e1051a5577 Mon Sep 17 00:00:00 2001 From: Mattias Buelens Date: Sat, 3 Dec 2016 17:52:04 +0100 Subject: [PATCH 08/17] Fix elided syntax kinds falling through to visitConstructor in TypeScript transformer. --- src/compiler/transformers/ts.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 6af8a2635993f..749ea46c8f20c 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -345,6 +345,7 @@ namespace ts { case SyntaxKind.PropertyDeclaration: // TypeScript property declarations are elided. + return undefined; case SyntaxKind.Constructor: return visitConstructor(node); From c538f1fdf7311e63775d10e44b2e7011e30e462e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 3 Dec 2016 11:03:18 -0800 Subject: [PATCH 09/17] Classify mapped type inferences as secondary --- src/compiler/checker.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 81f982e4dc069..adeb7a76376c1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8706,12 +8706,15 @@ namespace ts { if (constraintType.flags & TypeFlags.Index) { // We're inferring from some source type S to a homomorphic mapped type { [P in keyof T]: X }, // where T is a type variable. Use inferTypeForHomomorphicMappedType to infer a suitable source - // type and then infer from that type to T. + // type and then make a secondary inference from that type to T. We make a secondary inference + // such that direct inferences to T get priority over inferences to Partial, for example. const index = indexOf(typeVariables, (constraintType).type); if (index >= 0 && !typeInferences[index].isFixed) { const inferredType = inferTypeForHomomorphicMappedType(source, target); if (inferredType) { + inferiority++; inferFromTypes(inferredType, typeVariables[index]); + inferiority--; } } return; From 773c9a734fa297be2b7b3d73e6c8260b1ad1f396 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 3 Dec 2016 11:03:40 -0800 Subject: [PATCH 10/17] Add tests --- .../mapped/isomorphicMappedTypeInference.ts | 9 ++++++++- .../types/mapped/mappedTypeErrors.ts | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/cases/conformance/types/mapped/isomorphicMappedTypeInference.ts b/tests/cases/conformance/types/mapped/isomorphicMappedTypeInference.ts index 330d99dd7f152..14bb765a84011 100644 --- a/tests/cases/conformance/types/mapped/isomorphicMappedTypeInference.ts +++ b/tests/cases/conformance/types/mapped/isomorphicMappedTypeInference.ts @@ -145,4 +145,11 @@ var g1 = applySpec({ }); // Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } } -var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); \ No newline at end of file +var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); + +// Repro from #12633 + +const foo = (object: T, partial: Partial) => object; +let o = {a: 5, b: 7}; +foo(o, {b: 9}); +o = foo(o, {b: 9}); \ No newline at end of file diff --git a/tests/cases/conformance/types/mapped/mappedTypeErrors.ts b/tests/cases/conformance/types/mapped/mappedTypeErrors.ts index b318cde3aab06..a198d4e0dc181 100644 --- a/tests/cases/conformance/types/mapped/mappedTypeErrors.ts +++ b/tests/cases/conformance/types/mapped/mappedTypeErrors.ts @@ -67,4 +67,21 @@ function f11() { function f12() { var x: { [P in keyof T]: T[P] }; var x: { [P in keyof T]: T[P][] }; // Error +} + +// Check that inferences to mapped types are secondary + +declare function objAndReadonly(primary: T, secondary: Readonly): T; +declare function objAndPartial(primary: T, secondary: Partial): T; + +function f20() { + let x1 = objAndReadonly({ x: 0, y: 0 }, { x: 1 }); // Error + let x2 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1 }); + let x3 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error +} + +function f21() { + let x1 = objAndPartial({ x: 0, y: 0 }, { x: 1 }); + let x2 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1 }); + let x3 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error } \ No newline at end of file From 970c4aa4f7dbba9bddc24df43136ee86df03a1dc Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 3 Dec 2016 11:03:46 -0800 Subject: [PATCH 11/17] Accept new baselines --- .../isomorphicMappedTypeInference.js | 19 ++++++++- .../isomorphicMappedTypeInference.symbols | 28 +++++++++++++ .../isomorphicMappedTypeInference.types | 39 +++++++++++++++++++ .../reference/mappedTypeErrors.errors.txt | 34 +++++++++++++++- tests/baselines/reference/mappedTypeErrors.js | 31 +++++++++++++++ 5 files changed, 149 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/isomorphicMappedTypeInference.js b/tests/baselines/reference/isomorphicMappedTypeInference.js index 0ed2285ed04ba..560c926877a92 100644 --- a/tests/baselines/reference/isomorphicMappedTypeInference.js +++ b/tests/baselines/reference/isomorphicMappedTypeInference.js @@ -143,7 +143,14 @@ var g1 = applySpec({ }); // Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } } -var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); +var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); + +// Repro from #12633 + +const foo = (object: T, partial: Partial) => object; +let o = {a: 5, b: 7}; +foo(o, {b: 9}); +o = foo(o, {b: 9}); //// [isomorphicMappedTypeInference.js] function box(x) { @@ -244,6 +251,11 @@ var g1 = applySpec({ }); // Infers g2: (...args: any[]) => { foo: { bar: { baz: boolean } } } var g2 = applySpec({ foo: { bar: { baz: function (x) { return true; } } } }); +// Repro from #12633 +var foo = function (object, partial) { return object; }; +var o = { a: 5, b: 7 }; +foo(o, { b: 9 }); +o = foo(o, { b: 9 }); //// [isomorphicMappedTypeInference.d.ts] @@ -311,3 +323,8 @@ declare var g2: (...args: any[]) => { }; }; }; +declare const foo: (object: T, partial: Partial) => T; +declare let o: { + a: number; + b: number; +}; diff --git a/tests/baselines/reference/isomorphicMappedTypeInference.symbols b/tests/baselines/reference/isomorphicMappedTypeInference.symbols index 92bcbb697ec53..93ce825c35d26 100644 --- a/tests/baselines/reference/isomorphicMappedTypeInference.symbols +++ b/tests/baselines/reference/isomorphicMappedTypeInference.symbols @@ -459,3 +459,31 @@ var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); >baz : Symbol(baz, Decl(isomorphicMappedTypeInference.ts, 144, 34)) >x : Symbol(x, Decl(isomorphicMappedTypeInference.ts, 144, 41)) +// Repro from #12633 + +const foo = (object: T, partial: Partial) => object; +>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 148, 5)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 148, 13)) +>object : Symbol(object, Decl(isomorphicMappedTypeInference.ts, 148, 16)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 148, 13)) +>partial : Symbol(partial, Decl(isomorphicMappedTypeInference.ts, 148, 26)) +>Partial : Symbol(Partial, Decl(lib.d.ts, --, --)) +>T : Symbol(T, Decl(isomorphicMappedTypeInference.ts, 148, 13)) +>object : Symbol(object, Decl(isomorphicMappedTypeInference.ts, 148, 16)) + +let o = {a: 5, b: 7}; +>o : Symbol(o, Decl(isomorphicMappedTypeInference.ts, 149, 3)) +>a : Symbol(a, Decl(isomorphicMappedTypeInference.ts, 149, 9)) +>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 149, 14)) + +foo(o, {b: 9}); +>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 148, 5)) +>o : Symbol(o, Decl(isomorphicMappedTypeInference.ts, 149, 3)) +>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 150, 8)) + +o = foo(o, {b: 9}); +>o : Symbol(o, Decl(isomorphicMappedTypeInference.ts, 149, 3)) +>foo : Symbol(foo, Decl(isomorphicMappedTypeInference.ts, 148, 5)) +>o : Symbol(o, Decl(isomorphicMappedTypeInference.ts, 149, 3)) +>b : Symbol(b, Decl(isomorphicMappedTypeInference.ts, 151, 12)) + diff --git a/tests/baselines/reference/isomorphicMappedTypeInference.types b/tests/baselines/reference/isomorphicMappedTypeInference.types index b4d1383071bb7..ca20e06e11352 100644 --- a/tests/baselines/reference/isomorphicMappedTypeInference.types +++ b/tests/baselines/reference/isomorphicMappedTypeInference.types @@ -546,3 +546,42 @@ var g2 = applySpec({ foo: { bar: { baz: (x: any) => true } } }); >x : any >true : true +// Repro from #12633 + +const foo = (object: T, partial: Partial) => object; +>foo : (object: T, partial: Partial) => T +>(object: T, partial: Partial) => object : (object: T, partial: Partial) => T +>T : T +>object : T +>T : T +>partial : Partial +>Partial : Partial +>T : T +>object : T + +let o = {a: 5, b: 7}; +>o : { a: number; b: number; } +>{a: 5, b: 7} : { a: number; b: number; } +>a : number +>5 : 5 +>b : number +>7 : 7 + +foo(o, {b: 9}); +>foo(o, {b: 9}) : { a: number; b: number; } +>foo : (object: T, partial: Partial) => T +>o : { a: number; b: number; } +>{b: 9} : { b: number; } +>b : number +>9 : 9 + +o = foo(o, {b: 9}); +>o = foo(o, {b: 9}) : { a: number; b: number; } +>o : { a: number; b: number; } +>foo(o, {b: 9}) : { a: number; b: number; } +>foo : (object: T, partial: Partial) => T +>o : { a: number; b: number; } +>{b: 9} : { b: number; } +>b : number +>9 : 9 + diff --git a/tests/baselines/reference/mappedTypeErrors.errors.txt b/tests/baselines/reference/mappedTypeErrors.errors.txt index 945652bf92956..918c6627f6621 100644 --- a/tests/baselines/reference/mappedTypeErrors.errors.txt +++ b/tests/baselines/reference/mappedTypeErrors.errors.txt @@ -20,9 +20,15 @@ tests/cases/conformance/types/mapped/mappedTypeErrors.ts(60,9): error TS2403: Su tests/cases/conformance/types/mapped/mappedTypeErrors.ts(61,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]: T[P]; }'. tests/cases/conformance/types/mapped/mappedTypeErrors.ts(62,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ readonly [P in keyof T]?: T[P] | undefined; }'. tests/cases/conformance/types/mapped/mappedTypeErrors.ts(67,9): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]: T[P][]; }'. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(76,45): error TS2345: Argument of type '{ x: number; }' is not assignable to parameter of type 'Readonly<{ x: number; y: number; }>'. + Property 'y' is missing in type '{ x: number; }'. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(78,59): error TS2345: Argument of type '{ x: number; y: number; z: number; }' is not assignable to parameter of type 'Readonly<{ x: number; y: number; }>'. + Object literal may only specify known properties, and 'z' does not exist in type 'Readonly<{ x: number; y: number; }>'. +tests/cases/conformance/types/mapped/mappedTypeErrors.ts(84,58): error TS2345: Argument of type '{ x: number; y: number; z: number; }' is not assignable to parameter of type 'Partial<{ x: number; y: number; }>'. + Object literal may only specify known properties, and 'z' does not exist in type 'Partial<{ x: number; y: number; }>'. -==== tests/cases/conformance/types/mapped/mappedTypeErrors.ts (14 errors) ==== +==== tests/cases/conformance/types/mapped/mappedTypeErrors.ts (17 errors) ==== interface Shape { name: string; @@ -126,4 +132,30 @@ tests/cases/conformance/types/mapped/mappedTypeErrors.ts(67,9): error TS2403: Su var x: { [P in keyof T]: T[P][] }; // Error ~ !!! error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type '{ [P in keyof T]: T[P]; }', but here has type '{ [P in keyof T]: T[P][]; }'. + } + + // Check that inferences to mapped types are secondary + + declare function objAndReadonly(primary: T, secondary: Readonly): T; + declare function objAndPartial(primary: T, secondary: Partial): T; + + function f20() { + let x1 = objAndReadonly({ x: 0, y: 0 }, { x: 1 }); // Error + ~~~~~~~~ +!!! error TS2345: Argument of type '{ x: number; }' is not assignable to parameter of type 'Readonly<{ x: number; y: number; }>'. +!!! error TS2345: Property 'y' is missing in type '{ x: number; }'. + let x2 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1 }); + let x3 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error + ~~~~ +!!! error TS2345: Argument of type '{ x: number; y: number; z: number; }' is not assignable to parameter of type 'Readonly<{ x: number; y: number; }>'. +!!! error TS2345: Object literal may only specify known properties, and 'z' does not exist in type 'Readonly<{ x: number; y: number; }>'. + } + + function f21() { + let x1 = objAndPartial({ x: 0, y: 0 }, { x: 1 }); + let x2 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1 }); + let x3 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error + ~~~~ +!!! error TS2345: Argument of type '{ x: number; y: number; z: number; }' is not assignable to parameter of type 'Partial<{ x: number; y: number; }>'. +!!! error TS2345: Object literal may only specify known properties, and 'z' does not exist in type 'Partial<{ x: number; y: number; }>'. } \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeErrors.js b/tests/baselines/reference/mappedTypeErrors.js index cb840da32f7ba..4d2eadbafe9a6 100644 --- a/tests/baselines/reference/mappedTypeErrors.js +++ b/tests/baselines/reference/mappedTypeErrors.js @@ -66,6 +66,23 @@ function f11() { function f12() { var x: { [P in keyof T]: T[P] }; var x: { [P in keyof T]: T[P][] }; // Error +} + +// Check that inferences to mapped types are secondary + +declare function objAndReadonly(primary: T, secondary: Readonly): T; +declare function objAndPartial(primary: T, secondary: Partial): T; + +function f20() { + let x1 = objAndReadonly({ x: 0, y: 0 }, { x: 1 }); // Error + let x2 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1 }); + let x3 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error +} + +function f21() { + let x1 = objAndPartial({ x: 0, y: 0 }, { x: 1 }); + let x2 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1 }); + let x3 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error } //// [mappedTypeErrors.js] @@ -97,6 +114,16 @@ function f12() { var x; var x; // Error } +function f20() { + var x1 = objAndReadonly({ x: 0, y: 0 }, { x: 1 }); // Error + var x2 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1 }); + var x3 = objAndReadonly({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error +} +function f21() { + var x1 = objAndPartial({ x: 0, y: 0 }, { x: 1 }); + var x2 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1 }); + var x3 = objAndPartial({ x: 0, y: 0 }, { x: 1, y: 1, z: 1 }); // Error +} //// [mappedTypeErrors.d.ts] @@ -137,3 +164,7 @@ declare function f4(x: T): void; declare function f10(): void; declare function f11(): void; declare function f12(): void; +declare function objAndReadonly(primary: T, secondary: Readonly): T; +declare function objAndPartial(primary: T, secondary: Partial): T; +declare function f20(): void; +declare function f21(): void; From b8762111118f50ac2f7624a4e8508bff1257c41b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 3 Dec 2016 17:18:43 -0800 Subject: [PATCH 12/17] Property handle union/intersection types in type variable checks --- src/compiler/checker.ts | 23 ++++++++++------------- src/compiler/types.ts | 4 +++- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 81f982e4dc069..dc208f5613edd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4523,7 +4523,7 @@ namespace ts { // 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.TypeParameter ? getApparentType(constraintType) : constraintType; + const keyType = constraintType.flags & TypeFlags.TypeVariable ? getApparentType(constraintType) : constraintType; const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((keyType).type)) : keyType; forEachType(iterationType, t => { // Create a mapper from T to the current iteration type constituent. Then, if the @@ -4579,7 +4579,7 @@ namespace ts { function isGenericMappedType(type: Type) { if (getObjectFlags(type) & ObjectFlags.Mapped) { const constraintType = getConstraintTypeFromMappedType(type); - return !!(constraintType.flags & (TypeFlags.TypeParameter | TypeFlags.Index)); + return maybeTypeOfKind(constraintType, TypeFlags.TypeVariable | TypeFlags.Index); } return false; } @@ -5912,7 +5912,7 @@ namespace ts { return links.resolvedType; } - function getIndexTypeForTypeVariable(type: TypeVariable) { + function getIndexTypeForGenericType(type: TypeVariable | UnionOrIntersectionType) { if (!type.resolvedIndexType) { type.resolvedIndexType = createType(TypeFlags.Index); type.resolvedIndexType.type = type; @@ -5931,7 +5931,7 @@ namespace ts { } function getIndexType(type: Type): Type { - return type.flags & TypeFlags.TypeVariable ? getIndexTypeForTypeVariable(type) : + return maybeTypeOfKind(type, TypeFlags.TypeVariable) ? getIndexTypeForGenericType(type) : getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(type) : type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromPropertyNames(type); @@ -6032,14 +6032,11 @@ namespace ts { } function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) { - if (indexType.flags & TypeFlags.TypeVariable || - objectType.flags & TypeFlags.TypeVariable && indexType.flags & TypeFlags.Index || - isGenericMappedType(objectType)) { - // If the object type is a type variable (a type parameter or another indexed access type), if the - // index type is a type variable or an index type, or if the object type is a mapped type with a - // generic constraint, we are performing a higher-order index access where we cannot meaningfully - // access the properties of the object type. In those cases, we first check that the index type is - // assignable to 'keyof T' for the object type. + if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) || isGenericMappedType(objectType)) { + // If the index type is generic or if the object type is a mapped type with a generic constraint, + // we are performing a higher-order index access where we cannot meaningfully access the properties + // of the object type. In those cases, we first check that the index type is assignable to 'keyof T' + // for the object type. if (accessNode) { if (!isTypeAssignableTo(indexType, getIndexType(objectType))) { error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType)); @@ -6539,7 +6536,7 @@ namespace ts { // union type A | undefined, we produce { [P in keyof A]: X } | undefined. const constraintType = getConstraintTypeFromMappedType(type); if (constraintType.flags & TypeFlags.Index) { - const typeVariable = (constraintType).type; + const typeVariable = (constraintType).type; const mappedTypeVariable = instantiateType(typeVariable, mapper); if (typeVariable !== mappedTypeVariable) { return mapType(mappedTypeVariable, t => { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index aa96b35d758cc..0ad84d11c35f4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2908,6 +2908,8 @@ namespace ts { /* @internal */ resolvedProperties: SymbolTable; // Cache of resolved properties /* @internal */ + resolvedIndexType: IndexType; + /* @internal */ couldContainTypeVariables: boolean; } @@ -2991,7 +2993,7 @@ namespace ts { // keyof T types (TypeFlags.Index) export interface IndexType extends Type { - type: TypeVariable; + type: TypeVariable | UnionOrIntersectionType; } export const enum SignatureKind { From b4836e3d3d9bf47951d0e8357c164dd36415b1f0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 3 Dec 2016 17:19:10 -0800 Subject: [PATCH 13/17] Add tests --- .../types/keyof/keyofAndIndexedAccess.ts | 65 ++++++++++++++++++- .../keyof/keyofAndIndexedAccessErrors.ts | 11 ++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts index 3cad745402f95..3a2bb912c19a4 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts @@ -1,3 +1,4 @@ +// @strictNullChecks: true // @declaration: true class Shape { @@ -219,6 +220,36 @@ function f60(source: T, target: T) { } } +function f70(func: (k1: keyof (T | U), k2: keyof (T & U)) => void) { + func<{ a: any, b: any }, { a: any, c: any }>('a', 'a'); + func<{ a: any, b: any }, { a: any, c: any }>('a', 'b'); + func<{ a: any, b: any }, { a: any, c: any }>('a', 'c'); +} + +function f71(func: (x: T, y: U) => Partial) { + let x = func({ a: 1, b: "hello" }, { c: true }); + x.a; // number | undefined + x.b; // string | undefined + x.c; // boolean | undefined +} + +function f72(func: (x: T, y: U, k: K) => (T & U)[K]) { + let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number + let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string + let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean +} + +function f73(func: (x: T, y: U, k: K) => (T & U)[K]) { + let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number + let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string + let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean +} + +function f74(func: (x: T, y: U, k: K) => (T | U)[K]) { + let a = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a'); // number + let b = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b'); // string | boolean +} + // Repros from #12011 class Base { @@ -291,4 +322,36 @@ var empty = one(() => {}) // inferred as {}, expected type Handlers = { [K in keyof T]: (t: T[K]) => void } declare function on(handlerHash: Handlers): T var hashOfEmpty1 = on({ test: () => {} }); // {} -var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } \ No newline at end of file +var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } + +// Repro from #12624 + +interface Options1 { + data?: Data + computed?: Computed; +} + +declare class Component1 { + constructor(options: Options1); + get(key: K): (Data & Computed)[K]; +} + +let c1 = new Component1({ + data: { + hello: "" + } +}); + +c1.get("hello"); + +// Repro from #12625 + +interface Options2 { + data?: Data + computed?: Computed; +} + +declare class Component2 { + constructor(options: Options2); + get(key: K): (Data & Computed)[K]; +} \ No newline at end of file diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts index bc2ddc31a1920..cbbc9a5220086 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts @@ -65,4 +65,15 @@ function f10(shape: Shape) { setProperty(shape, "name", "rectangle"); setProperty(shape, "size", 10); // Error setProperty(shape, cond ? "name" : "size", 10); // Error +} + +function f20(k1: keyof (T | U), k2: keyof (T & U), o1: T | U, o2: T & U) { + o1[k1]; + o1[k2]; // Error + o2[k1]; + o2[k2]; + o1 = o2; + o2 = o1; // Error + k1 = k2; // Error + k2 = k1; } \ No newline at end of file From 3aa05b27e93291e965bca53395f3099f735c0bf9 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 3 Dec 2016 17:19:30 -0800 Subject: [PATCH 14/17] Accept new baselines --- .../reference/keyofAndIndexedAccess.js | 119 +++- .../reference/keyofAndIndexedAccess.symbols | 615 +++++++++++++----- .../reference/keyofAndIndexedAccess.types | 362 ++++++++++- .../keyofAndIndexedAccessErrors.errors.txt | 26 +- .../reference/keyofAndIndexedAccessErrors.js | 21 + 5 files changed, 971 insertions(+), 172 deletions(-) diff --git a/tests/baselines/reference/keyofAndIndexedAccess.js b/tests/baselines/reference/keyofAndIndexedAccess.js index 0e630be9a4392..1a0b90f316354 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.js +++ b/tests/baselines/reference/keyofAndIndexedAccess.js @@ -219,6 +219,36 @@ function f60(source: T, target: T) { } } +function f70(func: (k1: keyof (T | U), k2: keyof (T & U)) => void) { + func<{ a: any, b: any }, { a: any, c: any }>('a', 'a'); + func<{ a: any, b: any }, { a: any, c: any }>('a', 'b'); + func<{ a: any, b: any }, { a: any, c: any }>('a', 'c'); +} + +function f71(func: (x: T, y: U) => Partial) { + let x = func({ a: 1, b: "hello" }, { c: true }); + x.a; // number | undefined + x.b; // string | undefined + x.c; // boolean | undefined +} + +function f72(func: (x: T, y: U, k: K) => (T & U)[K]) { + let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number + let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string + let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean +} + +function f73(func: (x: T, y: U, k: K) => (T & U)[K]) { + let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number + let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string + let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean +} + +function f74(func: (x: T, y: U, k: K) => (T | U)[K]) { + let a = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a'); // number + let b = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b'); // string | boolean +} + // Repros from #12011 class Base { @@ -291,7 +321,39 @@ var empty = one(() => {}) // inferred as {}, expected type Handlers = { [K in keyof T]: (t: T[K]) => void } declare function on(handlerHash: Handlers): T var hashOfEmpty1 = on({ test: () => {} }); // {} -var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } +var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } + +// Repro from #12624 + +interface Options1 { + data?: Data + computed?: Computed; +} + +declare class Component1 { + constructor(options: Options1); + get(key: K): (Data & Computed)[K]; +} + +let c1 = new Component1({ + data: { + hello: "" + } +}); + +c1.get("hello"); + +// Repro from #12625 + +interface Options2 { + data?: Data + computed?: Computed; +} + +declare class Component2 { + constructor(options: Options2); + get(key: K): (Data & Computed)[K]; +} //// [keyofAndIndexedAccess.js] var __extends = (this && this.__extends) || function (d, b) { @@ -438,6 +500,31 @@ function f60(source, target) { target[k] = source[k]; } } +function f70(func) { + func('a', 'a'); + func('a', 'b'); + func('a', 'c'); +} +function f71(func) { + var x = func({ a: 1, b: "hello" }, { c: true }); + x.a; // number | undefined + x.b; // string | undefined + x.c; // boolean | undefined +} +function f72(func) { + var a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number + var b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string + var c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean +} +function f73(func) { + var a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number + var b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string + var c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean +} +function f74(func) { + var a = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a'); // number + var b = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b'); // string | boolean +} // Repros from #12011 var Base = (function () { function Base() { @@ -496,6 +583,12 @@ var assignTo2 = function (object, key1, key2) { var empty = one(function () { }); // inferred as {}, expected var hashOfEmpty1 = on({ test: function () { } }); // {} var hashOfEmpty2 = on({ test: function (x) { } }); // { test: boolean } +var c1 = new Component1({ + data: { + hello: "" + } +}); +c1.get("hello"); //// [keyofAndIndexedAccess.d.ts] @@ -603,6 +696,11 @@ declare function f53(obj: { declare function f54(obj: T, key: keyof T): void; declare function f55(obj: T, key: K): void; declare function f60(source: T, target: T): void; +declare function f70(func: (k1: keyof (T | U), k2: keyof (T & U)) => void): void; +declare function f71(func: (x: T, y: U) => Partial): void; +declare function f72(func: (x: T, y: U, k: K) => (T & U)[K]): void; +declare function f73(func: (x: T, y: U, k: K) => (T & U)[K]): void; +declare function f74(func: (x: T, y: U, k: K) => (T | U)[K]): void; declare class Base { get(prop: K): this[K]; set(prop: K, value: this[K]): void; @@ -640,3 +738,22 @@ declare var hashOfEmpty1: {}; declare var hashOfEmpty2: { test: boolean; }; +interface Options1 { + data?: Data; + computed?: Computed; +} +declare class Component1 { + constructor(options: Options1); + get(key: K): (Data & Computed)[K]; +} +declare let c1: Component1<{ + hello: string; +}, {}>; +interface Options2 { + data?: Data; + computed?: Computed; +} +declare class Component2 { + constructor(options: Options2); + get(key: K): (Data & Computed)[K]; +} diff --git a/tests/baselines/reference/keyofAndIndexedAccess.symbols b/tests/baselines/reference/keyofAndIndexedAccess.symbols index e67db316dad7c..bc9e53cdd7ebe 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccess.symbols @@ -766,278 +766,557 @@ function f60(source: T, target: T) { } } +function f70(func: (k1: keyof (T | U), k2: keyof (T & U)) => void) { +>f70 : Symbol(f70, Decl(keyofAndIndexedAccess.ts, 218, 1)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 220, 13)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 220, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 220, 22)) +>k1 : Symbol(k1, Decl(keyofAndIndexedAccess.ts, 220, 26)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 220, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 220, 22)) +>k2 : Symbol(k2, Decl(keyofAndIndexedAccess.ts, 220, 44)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 220, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 220, 22)) + + func<{ a: any, b: any }, { a: any, c: any }>('a', 'a'); +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 220, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 221, 10)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 221, 18)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 221, 30)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 221, 38)) + + func<{ a: any, b: any }, { a: any, c: any }>('a', 'b'); +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 220, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 222, 10)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 222, 18)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 222, 30)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 222, 38)) + + func<{ a: any, b: any }, { a: any, c: any }>('a', 'c'); +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 220, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 223, 10)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 223, 18)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 223, 30)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 223, 38)) +} + +function f71(func: (x: T, y: U) => Partial) { +>f71 : Symbol(f71, Decl(keyofAndIndexedAccess.ts, 224, 1)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 226, 13)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 226, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 226, 22)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 226, 26)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 226, 20)) +>y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 226, 31)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 226, 22)) +>Partial : Symbol(Partial, Decl(lib.d.ts, --, --)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 226, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 226, 22)) + + let x = func({ a: 1, b: "hello" }, { c: true }); +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 227, 7)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 226, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 227, 18)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 227, 24)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 227, 40)) + + x.a; // number | undefined +>x.a : Symbol(a) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 227, 7)) +>a : Symbol(a) + + x.b; // string | undefined +>x.b : Symbol(b) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 227, 7)) +>b : Symbol(b) + + x.c; // boolean | undefined +>x.c : Symbol(c) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 227, 7)) +>c : Symbol(c) +} + +function f72(func: (x: T, y: U, k: K) => (T & U)[K]) { +>f72 : Symbol(f72, Decl(keyofAndIndexedAccess.ts, 231, 1)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 233, 13)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 233, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 233, 22)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 233, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 233, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 233, 22)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 233, 55)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 233, 20)) +>y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 233, 60)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 233, 22)) +>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 233, 66)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 233, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 233, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 233, 22)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 233, 25)) + + let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 234, 7)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 233, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 234, 18)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 234, 24)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 234, 40)) + + let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 235, 7)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 233, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 235, 18)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 235, 24)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 235, 40)) + + let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 236, 7)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 233, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 236, 18)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 236, 24)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 236, 40)) +} + +function f73(func: (x: T, y: U, k: K) => (T & U)[K]) { +>f73 : Symbol(f73, Decl(keyofAndIndexedAccess.ts, 237, 1)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 239, 13)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 239, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 239, 22)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 239, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 239, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 239, 22)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 239, 51)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 239, 20)) +>y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 239, 56)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 239, 22)) +>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 239, 62)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 239, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 239, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 239, 22)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 239, 25)) + + let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 240, 7)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 239, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 240, 18)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 240, 24)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 240, 40)) + + let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 241, 7)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 239, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 241, 18)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 241, 24)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 241, 40)) + + let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 242, 7)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 239, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 242, 18)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 242, 24)) +>c : Symbol(c, Decl(keyofAndIndexedAccess.ts, 242, 40)) +} + +function f74(func: (x: T, y: U, k: K) => (T | U)[K]) { +>f74 : Symbol(f74, Decl(keyofAndIndexedAccess.ts, 243, 1)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 245, 13)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 245, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 245, 22)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 245, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 245, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 245, 22)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 245, 51)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 245, 20)) +>y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 245, 56)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 245, 22)) +>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 245, 62)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 245, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 245, 20)) +>U : Symbol(U, Decl(keyofAndIndexedAccess.ts, 245, 22)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 245, 25)) + + let a = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a'); // number +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 246, 7)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 245, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 246, 18)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 246, 24)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 246, 40)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 246, 46)) + + let b = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b'); // string | boolean +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 247, 7)) +>func : Symbol(func, Decl(keyofAndIndexedAccess.ts, 245, 13)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 247, 18)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 247, 24)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 247, 40)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 247, 46)) +} + // Repros from #12011 class Base { ->Base : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 218, 1)) +>Base : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 248, 1)) get(prop: K) { ->get : Symbol(Base.get, Decl(keyofAndIndexedAccess.ts, 222, 12)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 223, 8)) ->prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 223, 30)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 223, 8)) +>get : Symbol(Base.get, Decl(keyofAndIndexedAccess.ts, 252, 12)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 253, 8)) +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 253, 30)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 253, 8)) return this[prop]; ->this : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 218, 1)) ->prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 223, 30)) +>this : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 248, 1)) +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 253, 30)) } set(prop: K, value: this[K]) { ->set : Symbol(Base.set, Decl(keyofAndIndexedAccess.ts, 225, 5)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 226, 8)) ->prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 226, 30)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 226, 8)) ->value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 226, 38)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 226, 8)) +>set : Symbol(Base.set, Decl(keyofAndIndexedAccess.ts, 255, 5)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 256, 8)) +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 256, 30)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 256, 8)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 256, 38)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 256, 8)) this[prop] = value; ->this : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 218, 1)) ->prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 226, 30)) ->value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 226, 38)) +>this : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 248, 1)) +>prop : Symbol(prop, Decl(keyofAndIndexedAccess.ts, 256, 30)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 256, 38)) } } class Person extends Base { ->Person : Symbol(Person, Decl(keyofAndIndexedAccess.ts, 229, 1)) ->Base : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 218, 1)) +>Person : Symbol(Person, Decl(keyofAndIndexedAccess.ts, 259, 1)) +>Base : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 248, 1)) parts: number; ->parts : Symbol(Person.parts, Decl(keyofAndIndexedAccess.ts, 231, 27)) +>parts : Symbol(Person.parts, Decl(keyofAndIndexedAccess.ts, 261, 27)) constructor(parts: number) { ->parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 233, 16)) +>parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 263, 16)) super(); ->super : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 218, 1)) +>super : Symbol(Base, Decl(keyofAndIndexedAccess.ts, 248, 1)) this.set("parts", parts); ->this.set : Symbol(Base.set, Decl(keyofAndIndexedAccess.ts, 225, 5)) ->this : Symbol(Person, Decl(keyofAndIndexedAccess.ts, 229, 1)) ->set : Symbol(Base.set, Decl(keyofAndIndexedAccess.ts, 225, 5)) ->parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 233, 16)) +>this.set : Symbol(Base.set, Decl(keyofAndIndexedAccess.ts, 255, 5)) +>this : Symbol(Person, Decl(keyofAndIndexedAccess.ts, 259, 1)) +>set : Symbol(Base.set, Decl(keyofAndIndexedAccess.ts, 255, 5)) +>parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 263, 16)) } getParts() { ->getParts : Symbol(Person.getParts, Decl(keyofAndIndexedAccess.ts, 236, 5)) +>getParts : Symbol(Person.getParts, Decl(keyofAndIndexedAccess.ts, 266, 5)) return this.get("parts") ->this.get : Symbol(Base.get, Decl(keyofAndIndexedAccess.ts, 222, 12)) ->this : Symbol(Person, Decl(keyofAndIndexedAccess.ts, 229, 1)) ->get : Symbol(Base.get, Decl(keyofAndIndexedAccess.ts, 222, 12)) +>this.get : Symbol(Base.get, Decl(keyofAndIndexedAccess.ts, 252, 12)) +>this : Symbol(Person, Decl(keyofAndIndexedAccess.ts, 259, 1)) +>get : Symbol(Base.get, Decl(keyofAndIndexedAccess.ts, 252, 12)) } } class OtherPerson { ->OtherPerson : Symbol(OtherPerson, Decl(keyofAndIndexedAccess.ts, 240, 1)) +>OtherPerson : Symbol(OtherPerson, Decl(keyofAndIndexedAccess.ts, 270, 1)) parts: number; ->parts : Symbol(OtherPerson.parts, Decl(keyofAndIndexedAccess.ts, 242, 19)) +>parts : Symbol(OtherPerson.parts, Decl(keyofAndIndexedAccess.ts, 272, 19)) constructor(parts: number) { ->parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 244, 16)) +>parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 274, 16)) setProperty(this, "parts", parts); >setProperty : Symbol(setProperty, Decl(keyofAndIndexedAccess.ts, 81, 1)) ->this : Symbol(OtherPerson, Decl(keyofAndIndexedAccess.ts, 240, 1)) ->parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 244, 16)) +>this : Symbol(OtherPerson, Decl(keyofAndIndexedAccess.ts, 270, 1)) +>parts : Symbol(parts, Decl(keyofAndIndexedAccess.ts, 274, 16)) } getParts() { ->getParts : Symbol(OtherPerson.getParts, Decl(keyofAndIndexedAccess.ts, 246, 5)) +>getParts : Symbol(OtherPerson.getParts, Decl(keyofAndIndexedAccess.ts, 276, 5)) return getProperty(this, "parts") >getProperty : Symbol(getProperty, Decl(keyofAndIndexedAccess.ts, 77, 26)) ->this : Symbol(OtherPerson, Decl(keyofAndIndexedAccess.ts, 240, 1)) +>this : Symbol(OtherPerson, Decl(keyofAndIndexedAccess.ts, 270, 1)) } } // Modified repro from #12544 function path(obj: T, key1: K1): T[K1]; ->path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 254, 14)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 254, 16)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 254, 14)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 254, 37)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 254, 14)) ->key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 254, 44)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 254, 16)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 254, 14)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 254, 16)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 280, 1), Decl(keyofAndIndexedAccess.ts, 284, 62), Decl(keyofAndIndexedAccess.ts, 285, 100), Decl(keyofAndIndexedAccess.ts, 286, 142), Decl(keyofAndIndexedAccess.ts, 287, 59)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 284, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 284, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 284, 14)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 284, 37)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 284, 14)) +>key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 284, 44)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 284, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 284, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 284, 16)) function path(obj: T, key1: K1, key2: K2): T[K1][K2]; ->path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 255, 14)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 255, 16)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 255, 14)) ->K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 255, 36)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 255, 14)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 255, 16)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 255, 61)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 255, 14)) ->key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 255, 68)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 255, 16)) ->key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 255, 78)) ->K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 255, 36)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 255, 14)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 255, 16)) ->K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 255, 36)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 280, 1), Decl(keyofAndIndexedAccess.ts, 284, 62), Decl(keyofAndIndexedAccess.ts, 285, 100), Decl(keyofAndIndexedAccess.ts, 286, 142), Decl(keyofAndIndexedAccess.ts, 287, 59)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 285, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 285, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 285, 14)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 285, 36)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 285, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 285, 16)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 285, 61)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 285, 14)) +>key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 285, 68)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 285, 16)) +>key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 285, 78)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 285, 36)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 285, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 285, 16)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 285, 36)) function path(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3]; ->path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 256, 16)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) ->K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 256, 36)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 256, 16)) ->K3 : Symbol(K3, Decl(keyofAndIndexedAccess.ts, 256, 60)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 256, 16)) ->K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 256, 36)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 256, 89)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) ->key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 256, 96)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 256, 16)) ->key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 256, 106)) ->K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 256, 36)) ->key3 : Symbol(key3, Decl(keyofAndIndexedAccess.ts, 256, 116)) ->K3 : Symbol(K3, Decl(keyofAndIndexedAccess.ts, 256, 60)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 256, 14)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 256, 16)) ->K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 256, 36)) ->K3 : Symbol(K3, Decl(keyofAndIndexedAccess.ts, 256, 60)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 280, 1), Decl(keyofAndIndexedAccess.ts, 284, 62), Decl(keyofAndIndexedAccess.ts, 285, 100), Decl(keyofAndIndexedAccess.ts, 286, 142), Decl(keyofAndIndexedAccess.ts, 287, 59)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 286, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 14)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 286, 36)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 286, 16)) +>K3 : Symbol(K3, Decl(keyofAndIndexedAccess.ts, 286, 60)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 286, 16)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 286, 36)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 286, 89)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 14)) +>key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 286, 96)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 286, 16)) +>key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 286, 106)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 286, 36)) +>key3 : Symbol(key3, Decl(keyofAndIndexedAccess.ts, 286, 116)) +>K3 : Symbol(K3, Decl(keyofAndIndexedAccess.ts, 286, 60)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 14)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 286, 16)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 286, 36)) +>K3 : Symbol(K3, Decl(keyofAndIndexedAccess.ts, 286, 60)) function path(obj: any, ...keys: (string | number)[]): any; ->path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 257, 14)) ->keys : Symbol(keys, Decl(keyofAndIndexedAccess.ts, 257, 23)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 280, 1), Decl(keyofAndIndexedAccess.ts, 284, 62), Decl(keyofAndIndexedAccess.ts, 285, 100), Decl(keyofAndIndexedAccess.ts, 286, 142), Decl(keyofAndIndexedAccess.ts, 287, 59)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 287, 14)) +>keys : Symbol(keys, Decl(keyofAndIndexedAccess.ts, 287, 23)) function path(obj: any, ...keys: (string | number)[]): any { ->path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 258, 14)) ->keys : Symbol(keys, Decl(keyofAndIndexedAccess.ts, 258, 23)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 280, 1), Decl(keyofAndIndexedAccess.ts, 284, 62), Decl(keyofAndIndexedAccess.ts, 285, 100), Decl(keyofAndIndexedAccess.ts, 286, 142), Decl(keyofAndIndexedAccess.ts, 287, 59)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 288, 14)) +>keys : Symbol(keys, Decl(keyofAndIndexedAccess.ts, 288, 23)) let result = obj; ->result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 259, 7)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 258, 14)) +>result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 289, 7)) +>obj : Symbol(obj, Decl(keyofAndIndexedAccess.ts, 288, 14)) for (let k of keys) { ->k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 260, 12)) ->keys : Symbol(keys, Decl(keyofAndIndexedAccess.ts, 258, 23)) +>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 290, 12)) +>keys : Symbol(keys, Decl(keyofAndIndexedAccess.ts, 288, 23)) result = result[k]; ->result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 259, 7)) ->result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 259, 7)) ->k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 260, 12)) +>result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 289, 7)) +>result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 289, 7)) +>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 290, 12)) } return result; ->result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 259, 7)) +>result : Symbol(result, Decl(keyofAndIndexedAccess.ts, 289, 7)) } type Thing = { ->Thing : Symbol(Thing, Decl(keyofAndIndexedAccess.ts, 264, 1)) +>Thing : Symbol(Thing, Decl(keyofAndIndexedAccess.ts, 294, 1)) a: { x: number, y: string }, ->a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 266, 14)) ->x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 267, 8)) ->y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 267, 19)) +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 296, 14)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 297, 8)) +>y : Symbol(y, Decl(keyofAndIndexedAccess.ts, 297, 19)) b: boolean ->b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 267, 32)) +>b : Symbol(b, Decl(keyofAndIndexedAccess.ts, 297, 32)) }; function f1(thing: Thing) { ->f1 : Symbol(f1, Decl(keyofAndIndexedAccess.ts, 269, 2)) ->thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 272, 12)) ->Thing : Symbol(Thing, Decl(keyofAndIndexedAccess.ts, 264, 1)) +>f1 : Symbol(f1, Decl(keyofAndIndexedAccess.ts, 299, 2)) +>thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 302, 12)) +>Thing : Symbol(Thing, Decl(keyofAndIndexedAccess.ts, 294, 1)) let x1 = path(thing, 'a'); // { x: number, y: string } ->x1 : Symbol(x1, Decl(keyofAndIndexedAccess.ts, 273, 7)) ->path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) ->thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 272, 12)) +>x1 : Symbol(x1, Decl(keyofAndIndexedAccess.ts, 303, 7)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 280, 1), Decl(keyofAndIndexedAccess.ts, 284, 62), Decl(keyofAndIndexedAccess.ts, 285, 100), Decl(keyofAndIndexedAccess.ts, 286, 142), Decl(keyofAndIndexedAccess.ts, 287, 59)) +>thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 302, 12)) let x2 = path(thing, 'a', 'y'); // string ->x2 : Symbol(x2, Decl(keyofAndIndexedAccess.ts, 274, 7)) ->path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) ->thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 272, 12)) +>x2 : Symbol(x2, Decl(keyofAndIndexedAccess.ts, 304, 7)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 280, 1), Decl(keyofAndIndexedAccess.ts, 284, 62), Decl(keyofAndIndexedAccess.ts, 285, 100), Decl(keyofAndIndexedAccess.ts, 286, 142), Decl(keyofAndIndexedAccess.ts, 287, 59)) +>thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 302, 12)) let x3 = path(thing, 'b'); // boolean ->x3 : Symbol(x3, Decl(keyofAndIndexedAccess.ts, 275, 7)) ->path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) ->thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 272, 12)) +>x3 : Symbol(x3, Decl(keyofAndIndexedAccess.ts, 305, 7)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 280, 1), Decl(keyofAndIndexedAccess.ts, 284, 62), Decl(keyofAndIndexedAccess.ts, 285, 100), Decl(keyofAndIndexedAccess.ts, 286, 142), Decl(keyofAndIndexedAccess.ts, 287, 59)) +>thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 302, 12)) let x4 = path(thing, ...['a', 'x']); // any ->x4 : Symbol(x4, Decl(keyofAndIndexedAccess.ts, 276, 7)) ->path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 250, 1), Decl(keyofAndIndexedAccess.ts, 254, 62), Decl(keyofAndIndexedAccess.ts, 255, 100), Decl(keyofAndIndexedAccess.ts, 256, 142), Decl(keyofAndIndexedAccess.ts, 257, 59)) ->thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 272, 12)) +>x4 : Symbol(x4, Decl(keyofAndIndexedAccess.ts, 306, 7)) +>path : Symbol(path, Decl(keyofAndIndexedAccess.ts, 280, 1), Decl(keyofAndIndexedAccess.ts, 284, 62), Decl(keyofAndIndexedAccess.ts, 285, 100), Decl(keyofAndIndexedAccess.ts, 286, 142), Decl(keyofAndIndexedAccess.ts, 287, 59)) +>thing : Symbol(thing, Decl(keyofAndIndexedAccess.ts, 302, 12)) } // Repro from comment in #12114 const assignTo2 = (object: T, key1: K1, key2: K2) => ->assignTo2 : Symbol(assignTo2, Decl(keyofAndIndexedAccess.ts, 281, 5)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 281, 19)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 281, 21)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 281, 19)) ->K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 281, 41)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 281, 19)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 281, 21)) ->object : Symbol(object, Decl(keyofAndIndexedAccess.ts, 281, 66)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 281, 19)) ->key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 281, 76)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 281, 21)) ->key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 281, 86)) ->K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 281, 41)) +>assignTo2 : Symbol(assignTo2, Decl(keyofAndIndexedAccess.ts, 311, 5)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 311, 19)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 311, 21)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 311, 19)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 311, 41)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 311, 19)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 311, 21)) +>object : Symbol(object, Decl(keyofAndIndexedAccess.ts, 311, 66)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 311, 19)) +>key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 311, 76)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 311, 21)) +>key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 311, 86)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 311, 41)) (value: T[K1][K2]) => object[key1][key2] = value; ->value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 282, 5)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 281, 19)) ->K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 281, 21)) ->K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 281, 41)) ->object : Symbol(object, Decl(keyofAndIndexedAccess.ts, 281, 66)) ->key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 281, 76)) ->key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 281, 86)) ->value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 282, 5)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 312, 5)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 311, 19)) +>K1 : Symbol(K1, Decl(keyofAndIndexedAccess.ts, 311, 21)) +>K2 : Symbol(K2, Decl(keyofAndIndexedAccess.ts, 311, 41)) +>object : Symbol(object, Decl(keyofAndIndexedAccess.ts, 311, 66)) +>key1 : Symbol(key1, Decl(keyofAndIndexedAccess.ts, 311, 76)) +>key2 : Symbol(key2, Decl(keyofAndIndexedAccess.ts, 311, 86)) +>value : Symbol(value, Decl(keyofAndIndexedAccess.ts, 312, 5)) // Modified repro from #12573 declare function one(handler: (t: T) => void): T ->one : Symbol(one, Decl(keyofAndIndexedAccess.ts, 282, 53)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 21)) ->handler : Symbol(handler, Decl(keyofAndIndexedAccess.ts, 286, 24)) ->t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 286, 34)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 21)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 286, 21)) +>one : Symbol(one, Decl(keyofAndIndexedAccess.ts, 312, 53)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 316, 21)) +>handler : Symbol(handler, Decl(keyofAndIndexedAccess.ts, 316, 24)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 316, 34)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 316, 21)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 316, 21)) var empty = one(() => {}) // inferred as {}, expected ->empty : Symbol(empty, Decl(keyofAndIndexedAccess.ts, 287, 3)) ->one : Symbol(one, Decl(keyofAndIndexedAccess.ts, 282, 53)) +>empty : Symbol(empty, Decl(keyofAndIndexedAccess.ts, 317, 3)) +>one : Symbol(one, Decl(keyofAndIndexedAccess.ts, 312, 53)) type Handlers = { [K in keyof T]: (t: T[K]) => void } ->Handlers : Symbol(Handlers, Decl(keyofAndIndexedAccess.ts, 287, 25)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 289, 14)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 289, 22)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 289, 14)) ->t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 289, 38)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 289, 14)) ->K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 289, 22)) +>Handlers : Symbol(Handlers, Decl(keyofAndIndexedAccess.ts, 317, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 319, 14)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 319, 22)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 319, 14)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 319, 38)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 319, 14)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 319, 22)) declare function on(handlerHash: Handlers): T ->on : Symbol(on, Decl(keyofAndIndexedAccess.ts, 289, 56)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 290, 20)) ->handlerHash : Symbol(handlerHash, Decl(keyofAndIndexedAccess.ts, 290, 23)) ->Handlers : Symbol(Handlers, Decl(keyofAndIndexedAccess.ts, 287, 25)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 290, 20)) ->T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 290, 20)) +>on : Symbol(on, Decl(keyofAndIndexedAccess.ts, 319, 56)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 320, 20)) +>handlerHash : Symbol(handlerHash, Decl(keyofAndIndexedAccess.ts, 320, 23)) +>Handlers : Symbol(Handlers, Decl(keyofAndIndexedAccess.ts, 317, 25)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 320, 20)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 320, 20)) var hashOfEmpty1 = on({ test: () => {} }); // {} ->hashOfEmpty1 : Symbol(hashOfEmpty1, Decl(keyofAndIndexedAccess.ts, 291, 3)) ->on : Symbol(on, Decl(keyofAndIndexedAccess.ts, 289, 56)) ->test : Symbol(test, Decl(keyofAndIndexedAccess.ts, 291, 23)) +>hashOfEmpty1 : Symbol(hashOfEmpty1, Decl(keyofAndIndexedAccess.ts, 321, 3)) +>on : Symbol(on, Decl(keyofAndIndexedAccess.ts, 319, 56)) +>test : Symbol(test, Decl(keyofAndIndexedAccess.ts, 321, 23)) var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } ->hashOfEmpty2 : Symbol(hashOfEmpty2, Decl(keyofAndIndexedAccess.ts, 292, 3)) ->on : Symbol(on, Decl(keyofAndIndexedAccess.ts, 289, 56)) ->test : Symbol(test, Decl(keyofAndIndexedAccess.ts, 292, 23)) ->x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 292, 31)) +>hashOfEmpty2 : Symbol(hashOfEmpty2, Decl(keyofAndIndexedAccess.ts, 322, 3)) +>on : Symbol(on, Decl(keyofAndIndexedAccess.ts, 319, 56)) +>test : Symbol(test, Decl(keyofAndIndexedAccess.ts, 322, 23)) +>x : Symbol(x, Decl(keyofAndIndexedAccess.ts, 322, 31)) + +// Repro from #12624 + +interface Options1 { +>Options1 : Symbol(Options1, Decl(keyofAndIndexedAccess.ts, 322, 52)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 326, 19)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 326, 24)) + + data?: Data +>data : Symbol(Options1.data, Decl(keyofAndIndexedAccess.ts, 326, 36)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 326, 19)) + + computed?: Computed; +>computed : Symbol(Options1.computed, Decl(keyofAndIndexedAccess.ts, 327, 15)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 326, 24)) +} + +declare class Component1 { +>Component1 : Symbol(Component1, Decl(keyofAndIndexedAccess.ts, 329, 1)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 331, 25)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 331, 30)) + + constructor(options: Options1); +>options : Symbol(options, Decl(keyofAndIndexedAccess.ts, 332, 16)) +>Options1 : Symbol(Options1, Decl(keyofAndIndexedAccess.ts, 322, 52)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 331, 25)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 331, 30)) + + get(key: K): (Data & Computed)[K]; +>get : Symbol(Component1.get, Decl(keyofAndIndexedAccess.ts, 332, 51)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 333, 8)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 331, 25)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 331, 30)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 333, 43)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 333, 8)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 331, 25)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 331, 30)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 333, 8)) +} + +let c1 = new Component1({ +>c1 : Symbol(c1, Decl(keyofAndIndexedAccess.ts, 336, 3)) +>Component1 : Symbol(Component1, Decl(keyofAndIndexedAccess.ts, 329, 1)) + + data: { +>data : Symbol(data, Decl(keyofAndIndexedAccess.ts, 336, 25)) + + hello: "" +>hello : Symbol(hello, Decl(keyofAndIndexedAccess.ts, 337, 11)) + } +}); +c1.get("hello"); +>c1.get : Symbol(Component1.get, Decl(keyofAndIndexedAccess.ts, 332, 51)) +>c1 : Symbol(c1, Decl(keyofAndIndexedAccess.ts, 336, 3)) +>get : Symbol(Component1.get, Decl(keyofAndIndexedAccess.ts, 332, 51)) + +// Repro from #12625 + +interface Options2 { +>Options2 : Symbol(Options2, Decl(keyofAndIndexedAccess.ts, 342, 16)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 346, 19)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 346, 24)) + + data?: Data +>data : Symbol(Options2.data, Decl(keyofAndIndexedAccess.ts, 346, 36)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 346, 19)) + + computed?: Computed; +>computed : Symbol(Options2.computed, Decl(keyofAndIndexedAccess.ts, 347, 15)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 346, 24)) +} + +declare class Component2 { +>Component2 : Symbol(Component2, Decl(keyofAndIndexedAccess.ts, 349, 1)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 351, 25)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 351, 30)) + + constructor(options: Options2); +>options : Symbol(options, Decl(keyofAndIndexedAccess.ts, 352, 16)) +>Options2 : Symbol(Options2, Decl(keyofAndIndexedAccess.ts, 342, 16)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 351, 25)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 351, 30)) + + get(key: K): (Data & Computed)[K]; +>get : Symbol(Component2.get, Decl(keyofAndIndexedAccess.ts, 352, 51)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 353, 8)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 351, 25)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 351, 30)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 353, 47)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 353, 8)) +>Data : Symbol(Data, Decl(keyofAndIndexedAccess.ts, 351, 25)) +>Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 351, 30)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 353, 8)) +} diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index 9e1e7d4b4d171..7cd4b434af692 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -780,8 +780,8 @@ function f52(obj: { [x: string]: boolean }, k: keyof T, s: string, n: number) >n : number const x3 = obj[k]; ->x3 : boolean ->obj[k] : boolean +>x3 : { [x: string]: boolean; }[keyof T] +>obj[k] : { [x: string]: boolean; }[keyof T] >obj : { [x: string]: boolean; } >k : keyof T } @@ -888,6 +888,264 @@ function f60(source: T, target: T) { } } +function f70(func: (k1: keyof (T | U), k2: keyof (T & U)) => void) { +>f70 : (func: (k1: keyof (T | U), k2: keyof (T & U)) => void) => void +>func : (k1: keyof (T | U), k2: keyof (T & U)) => void +>T : T +>U : U +>k1 : keyof (T | U) +>T : T +>U : U +>k2 : keyof (T & U) +>T : T +>U : U + + func<{ a: any, b: any }, { a: any, c: any }>('a', 'a'); +>func<{ a: any, b: any }, { a: any, c: any }>('a', 'a') : void +>func : (k1: keyof (T | U), k2: keyof (T & U)) => void +>a : any +>b : any +>a : any +>c : any +>'a' : "a" +>'a' : "a" + + func<{ a: any, b: any }, { a: any, c: any }>('a', 'b'); +>func<{ a: any, b: any }, { a: any, c: any }>('a', 'b') : void +>func : (k1: keyof (T | U), k2: keyof (T & U)) => void +>a : any +>b : any +>a : any +>c : any +>'a' : "a" +>'b' : "b" + + func<{ a: any, b: any }, { a: any, c: any }>('a', 'c'); +>func<{ a: any, b: any }, { a: any, c: any }>('a', 'c') : void +>func : (k1: keyof (T | U), k2: keyof (T & U)) => void +>a : any +>b : any +>a : any +>c : any +>'a' : "a" +>'c' : "c" +} + +function f71(func: (x: T, y: U) => Partial) { +>f71 : (func: (x: T, y: U) => Partial) => void +>func : (x: T, y: U) => Partial +>T : T +>U : U +>x : T +>T : T +>y : U +>U : U +>Partial : Partial +>T : T +>U : U + + let x = func({ a: 1, b: "hello" }, { c: true }); +>x : Partial<{ a: number; b: string; } & { c: boolean; }> +>func({ a: 1, b: "hello" }, { c: true }) : Partial<{ a: number; b: string; } & { c: boolean; }> +>func : (x: T, y: U) => Partial +>{ a: 1, b: "hello" } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>"hello" : "hello" +>{ c: true } : { c: true; } +>c : boolean +>true : true + + x.a; // number | undefined +>x.a : number | undefined +>x : Partial<{ a: number; b: string; } & { c: boolean; }> +>a : number | undefined + + x.b; // string | undefined +>x.b : string | undefined +>x : Partial<{ a: number; b: string; } & { c: boolean; }> +>b : string | undefined + + x.c; // boolean | undefined +>x.c : boolean | undefined +>x : Partial<{ a: number; b: string; } & { c: boolean; }> +>c : boolean | undefined +} + +function f72(func: (x: T, y: U, k: K) => (T & U)[K]) { +>f72 : (func: (x: T, y: U, k: K) => (T & U)[K]) => void +>func : (x: T, y: U, k: K) => (T & U)[K] +>T : T +>U : U +>K : K +>T : T +>U : U +>x : T +>T : T +>y : U +>U : U +>k : K +>K : K +>T : T +>U : U +>K : K + + let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number +>a : number +>func({ a: 1, b: "hello" }, { c: true }, 'a') : number +>func : (x: T, y: U, k: K) => (T & U)[K] +>{ a: 1, b: "hello" } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>"hello" : "hello" +>{ c: true } : { c: true; } +>c : boolean +>true : true +>'a' : "a" + + let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string +>b : string +>func({ a: 1, b: "hello" }, { c: true }, 'b') : string +>func : (x: T, y: U, k: K) => (T & U)[K] +>{ a: 1, b: "hello" } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>"hello" : "hello" +>{ c: true } : { c: true; } +>c : boolean +>true : true +>'b' : "b" + + let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean +>c : boolean +>func({ a: 1, b: "hello" }, { c: true }, 'c') : boolean +>func : (x: T, y: U, k: K) => (T & U)[K] +>{ a: 1, b: "hello" } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>"hello" : "hello" +>{ c: true } : { c: true; } +>c : boolean +>true : true +>'c' : "c" +} + +function f73(func: (x: T, y: U, k: K) => (T & U)[K]) { +>f73 : (func: (x: T, y: U, k: K) => (T & U)[K]) => void +>func : (x: T, y: U, k: K) => (T & U)[K] +>T : T +>U : U +>K : K +>T : T +>U : U +>x : T +>T : T +>y : U +>U : U +>k : K +>K : K +>T : T +>U : U +>K : K + + let a = func({ a: 1, b: "hello" }, { c: true }, 'a'); // number +>a : number +>func({ a: 1, b: "hello" }, { c: true }, 'a') : number +>func : (x: T, y: U, k: K) => (T & U)[K] +>{ a: 1, b: "hello" } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>"hello" : "hello" +>{ c: true } : { c: true; } +>c : boolean +>true : true +>'a' : "a" + + let b = func({ a: 1, b: "hello" }, { c: true }, 'b'); // string +>b : string +>func({ a: 1, b: "hello" }, { c: true }, 'b') : string +>func : (x: T, y: U, k: K) => (T & U)[K] +>{ a: 1, b: "hello" } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>"hello" : "hello" +>{ c: true } : { c: true; } +>c : boolean +>true : true +>'b' : "b" + + let c = func({ a: 1, b: "hello" }, { c: true }, 'c'); // boolean +>c : boolean +>func({ a: 1, b: "hello" }, { c: true }, 'c') : boolean +>func : (x: T, y: U, k: K) => (T & U)[K] +>{ a: 1, b: "hello" } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>"hello" : "hello" +>{ c: true } : { c: true; } +>c : boolean +>true : true +>'c' : "c" +} + +function f74(func: (x: T, y: U, k: K) => (T | U)[K]) { +>f74 : (func: (x: T, y: U, k: K) => (T | U)[K]) => void +>func : (x: T, y: U, k: K) => (T | U)[K] +>T : T +>U : U +>K : K +>T : T +>U : U +>x : T +>T : T +>y : U +>U : U +>k : K +>K : K +>T : T +>U : U +>K : K + + let a = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a'); // number +>a : number +>func({ a: 1, b: "hello" }, { a: 2, b: true }, 'a') : number +>func : (x: T, y: U, k: K) => (T | U)[K] +>{ a: 1, b: "hello" } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>"hello" : "hello" +>{ a: 2, b: true } : { a: number; b: true; } +>a : number +>2 : 2 +>b : boolean +>true : true +>'a' : "a" + + let b = func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b'); // string | boolean +>b : string | boolean +>func({ a: 1, b: "hello" }, { a: 2, b: true }, 'b') : string | boolean +>func : (x: T, y: U, k: K) => (T | U)[K] +>{ a: 1, b: "hello" } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>"hello" : "hello" +>{ a: 2, b: true } : { a: number; b: true; } +>a : number +>2 : 2 +>b : boolean +>true : true +>'b' : "b" +} + // Repros from #12011 class Base { @@ -1202,3 +1460,103 @@ var hashOfEmpty2 = on({ test: (x: boolean) => {} }); // { test: boolean } >(x: boolean) => {} : (x: boolean) => void >x : boolean +// Repro from #12624 + +interface Options1 { +>Options1 : Options1 +>Data : Data +>Computed : Computed + + data?: Data +>data : Data | undefined +>Data : Data + + computed?: Computed; +>computed : Computed | undefined +>Computed : Computed +} + +declare class Component1 { +>Component1 : Component1 +>Data : Data +>Computed : Computed + + constructor(options: Options1); +>options : Options1 +>Options1 : Options1 +>Data : Data +>Computed : Computed + + get(key: K): (Data & Computed)[K]; +>get : (key: K) => (Data & Computed)[K] +>K : K +>Data : Data +>Computed : Computed +>key : K +>K : K +>Data : Data +>Computed : Computed +>K : K +} + +let c1 = new Component1({ +>c1 : Component1<{ hello: string; }, {}> +>new Component1({ data: { hello: "" }}) : Component1<{ hello: string; }, {}> +>Component1 : typeof Component1 +>{ data: { hello: "" }} : { data: { hello: string; }; } + + data: { +>data : { hello: string; } +>{ hello: "" } : { hello: string; } + + hello: "" +>hello : string +>"" : "" + } +}); + +c1.get("hello"); +>c1.get("hello") : string +>c1.get : (key: K) => ({ hello: string; } & {})[K] +>c1 : Component1<{ hello: string; }, {}> +>get : (key: K) => ({ hello: string; } & {})[K] +>"hello" : "hello" + +// Repro from #12625 + +interface Options2 { +>Options2 : Options2 +>Data : Data +>Computed : Computed + + data?: Data +>data : Data | undefined +>Data : Data + + computed?: Computed; +>computed : Computed | undefined +>Computed : Computed +} + +declare class Component2 { +>Component2 : Component2 +>Data : Data +>Computed : Computed + + constructor(options: Options2); +>options : Options2 +>Options2 : Options2 +>Data : Data +>Computed : Computed + + get(key: K): (Data & Computed)[K]; +>get : (key: K) => (Data & Computed)[K] +>K : K +>Data : Data +>Computed : Computed +>key : K +>K : K +>Data : Data +>Computed : Computed +>K : K +} diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt index 23b79c16627fc..1e88e2abc4341 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt @@ -21,9 +21,14 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(64,33): error tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(66,24): error TS2345: Argument of type '"size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(67,24): error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'. Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(72,5): error TS2536: Type 'keyof (T & U)' cannot be used to index type 'T | U'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(76,5): error TS2322: Type 'T | U' is not assignable to type 'T & U'. + Type 'T' is not assignable to type 'T & U'. + Type 'T' is not assignable to type 'U'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(77,5): error TS2322: Type 'keyof (T & U)' is not assignable to type 'keyof (T | U)'. -==== tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts (21 errors) ==== +==== tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts (24 errors) ==== class Shape { name: string; width: number; @@ -135,4 +140,23 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(67,24): error ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'. !!! error TS2345: Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'. + } + + function f20(k1: keyof (T | U), k2: keyof (T & U), o1: T | U, o2: T & U) { + o1[k1]; + o1[k2]; // Error + ~~~~~~ +!!! error TS2536: Type 'keyof (T & U)' cannot be used to index type 'T | U'. + o2[k1]; + o2[k2]; + o1 = o2; + o2 = o1; // Error + ~~ +!!! error TS2322: Type 'T | U' is not assignable to type 'T & U'. +!!! error TS2322: Type 'T' is not assignable to type 'T & U'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. + k1 = k2; // Error + ~~ +!!! error TS2322: Type 'keyof (T & U)' is not assignable to type 'keyof (T | U)'. + k2 = k1; } \ No newline at end of file diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.js b/tests/baselines/reference/keyofAndIndexedAccessErrors.js index 145d8e3f14898..556aca129711b 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.js +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.js @@ -66,6 +66,17 @@ function f10(shape: Shape) { setProperty(shape, "name", "rectangle"); setProperty(shape, "size", 10); // Error setProperty(shape, cond ? "name" : "size", 10); // Error +} + +function f20(k1: keyof (T | U), k2: keyof (T & U), o1: T | U, o2: T & U) { + o1[k1]; + o1[k2]; // Error + o2[k1]; + o2[k2]; + o1 = o2; + o2 = o1; // Error + k1 = k2; // Error + k2 = k1; } //// [keyofAndIndexedAccessErrors.js] @@ -88,3 +99,13 @@ function f10(shape) { setProperty(shape, "size", 10); // Error setProperty(shape, cond ? "name" : "size", 10); // Error } +function f20(k1, k2, o1, o2) { + o1[k1]; + o1[k2]; // Error + o2[k1]; + o2[k2]; + o1 = o2; + o2 = o1; // Error + k1 = k2; // Error + k2 = k1; +} From c52eb6cef3fc149b5e5729de5b2ed11e72e11cab Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 4 Dec 2016 07:43:10 -0800 Subject: [PATCH 15/17] Indexed access any[K] has type any --- src/compiler/checker.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 912db011904bf..903c8cfda1c20 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6033,6 +6033,9 @@ namespace ts { function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) { if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) || isGenericMappedType(objectType)) { + if (objectType.flags & TypeFlags.Any) { + return objectType; + } // If the index type is generic or if the object type is a mapped type with a generic constraint, // we are performing a higher-order index access where we cannot meaningfully access the properties // of the object type. In those cases, we first check that the index type is assignable to 'keyof T' From 95aed3f4ee1cb38ddb87aa7797724fe94739f726 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 4 Dec 2016 07:49:55 -0800 Subject: [PATCH 16/17] Add regression test --- .../conformance/types/keyof/keyofAndIndexedAccess.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts index 3a2bb912c19a4..08bcfb2820457 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts @@ -354,4 +354,15 @@ interface Options2 { declare class Component2 { constructor(options: Options2); get(key: K): (Data & Computed)[K]; +} + +// Repro from #12641 + +interface R { + p: number; +} + +function f(p: K) { + let a: any; + a[p].add; // any } \ No newline at end of file From ee172cfa39a6745ac13a300dc176daae462dd0da Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 4 Dec 2016 07:50:10 -0800 Subject: [PATCH 17/17] Accept new baselines --- .../reference/keyofAndIndexedAccess.js | 19 +++++++++++++ .../reference/keyofAndIndexedAccess.symbols | 24 +++++++++++++++++ .../reference/keyofAndIndexedAccess.types | 27 +++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/tests/baselines/reference/keyofAndIndexedAccess.js b/tests/baselines/reference/keyofAndIndexedAccess.js index 1a0b90f316354..a9a5ee29993e6 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.js +++ b/tests/baselines/reference/keyofAndIndexedAccess.js @@ -353,6 +353,17 @@ interface Options2 { declare class Component2 { constructor(options: Options2); get(key: K): (Data & Computed)[K]; +} + +// Repro from #12641 + +interface R { + p: number; +} + +function f(p: K) { + let a: any; + a[p].add; // any } //// [keyofAndIndexedAccess.js] @@ -589,6 +600,10 @@ var c1 = new Component1({ } }); c1.get("hello"); +function f(p) { + var a; + a[p].add; // any +} //// [keyofAndIndexedAccess.d.ts] @@ -757,3 +772,7 @@ declare class Component2 { constructor(options: Options2); get(key: K): (Data & Computed)[K]; } +interface R { + p: number; +} +declare function f(p: K): void; diff --git a/tests/baselines/reference/keyofAndIndexedAccess.symbols b/tests/baselines/reference/keyofAndIndexedAccess.symbols index bc9e53cdd7ebe..3d90b4ca276dd 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccess.symbols @@ -1320,3 +1320,27 @@ declare class Component2 { >Computed : Symbol(Computed, Decl(keyofAndIndexedAccess.ts, 351, 30)) >K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 353, 8)) } + +// Repro from #12641 + +interface R { +>R : Symbol(R, Decl(keyofAndIndexedAccess.ts, 354, 1)) + + p: number; +>p : Symbol(R.p, Decl(keyofAndIndexedAccess.ts, 358, 13)) +} + +function f(p: K) { +>f : Symbol(f, Decl(keyofAndIndexedAccess.ts, 360, 1)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 362, 11)) +>R : Symbol(R, Decl(keyofAndIndexedAccess.ts, 354, 1)) +>p : Symbol(p, Decl(keyofAndIndexedAccess.ts, 362, 30)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 362, 11)) + + let a: any; +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 363, 7)) + + a[p].add; // any +>a : Symbol(a, Decl(keyofAndIndexedAccess.ts, 363, 7)) +>p : Symbol(p, Decl(keyofAndIndexedAccess.ts, 362, 30)) +} diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index 7cd4b434af692..3b9846437764b 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -1560,3 +1560,30 @@ declare class Component2 { >Computed : Computed >K : K } + +// Repro from #12641 + +interface R { +>R : R + + p: number; +>p : number +} + +function f(p: K) { +>f : (p: K) => void +>K : K +>R : R +>p : K +>K : K + + let a: any; +>a : any + + a[p].add; // any +>a[p].add : any +>a[p] : any +>a : any +>p : K +>add : any +}