diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 725cdf4e8e493..1558dcd716ae4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13720,10 +13720,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return type.modifiersType; } + function getMappedTypeNodeModifiers(node: MappedTypeNode) { + return (node.readonlyToken ? node.readonlyToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeReadonly : MappedTypeModifiers.IncludeReadonly : 0) | + (node.questionToken ? node.questionToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeOptional : MappedTypeModifiers.IncludeOptional : 0); + } + function getMappedTypeModifiers(type: MappedType): MappedTypeModifiers { - const declaration = type.declaration; - return (declaration.readonlyToken ? declaration.readonlyToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeReadonly : MappedTypeModifiers.IncludeReadonly : 0) | - (declaration.questionToken ? declaration.questionToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeOptional : MappedTypeModifiers.IncludeOptional : 0); + return getMappedTypeNodeModifiers(type.declaration); } function getMappedTypeOptionality(type: MappedType): number { @@ -15881,14 +15884,27 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } // Given a homomorphic mapped type { [K in keyof T]: XXX }, where T is constrained to an array or tuple type, in the // template type XXX, K has an added constraint of number | `${number}`. - else if (type.flags & TypeFlags.TypeParameter && parent.kind === SyntaxKind.MappedType && node === (parent as MappedTypeNode).type) { - const mappedType = getTypeFromTypeNode(parent as TypeNode) as MappedType; - if (getTypeParameterFromMappedType(mappedType) === getActualTypeVariable(type)) { - const typeParameter = getHomomorphicTypeVariable(mappedType); - if (typeParameter) { - const constraint = getConstraintOfTypeParameter(typeParameter); - if (constraint && everyType(constraint, isArrayOrTupleType)) { - constraints = append(constraints, getUnionType([numberType, numericStringType])); + else if (type.flags & TypeFlags.TypeParameter && parent.kind === SyntaxKind.MappedType && node === (parent as MappedTypeNode).type && !(parent as MappedTypeNode).nameType) { + const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfDeclaration((parent as MappedTypeNode).typeParameter)); + const constraintType = getConstraintOfTypeParameter(typeParameter) || errorType; + const arrayOrTuple = getArrayOrTupleOriginIndexType(constraintType); + if (arrayOrTuple) { + if (isTupleType(arrayOrTuple)) { + constraints = append(constraints, getUnionType(map(getTypeArguments(arrayOrTuple), (_, i) => getStringLiteralType("" + i)))); + } + else { + constraints = append(constraints, getUnionType([numberType, numericStringType])); + } + } + else { + if (typeParameter === getActualTypeVariable(type)) { + const typeVariable = constraintType.flags & TypeFlags.Index && getActualTypeVariable((constraintType as IndexType).type); + const homomorphicTypeVariable = typeVariable && typeVariable.flags & TypeFlags.TypeParameter ? typeVariable as TypeParameter : undefined; + if (homomorphicTypeVariable) { + const constraint = getConstraintOfTypeParameter(homomorphicTypeVariable); + if (constraint && everyType(constraint, isArrayOrTupleType)) { + constraints = append(constraints, getUnionType([numberType, numericStringType])); + } } } } @@ -18152,17 +18168,58 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return links.resolvedType; } + function getArrayOrTupleOriginIndexType(type: Type) { + if (!(type.flags & TypeFlags.Union)) { + return; + } + const origin = (type as UnionType).origin; + if (!origin || !(origin.flags & TypeFlags.Index)) { + return; + } + const originType = (origin as IndexType).type; + return isArrayOrTupleType(originType) ? originType : undefined; + } + function getTypeFromMappedTypeNode(node: MappedTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { + // Eagerly resolve the constraint type which forces an error if the constraint type circularly + // references itself through one or more type aliases. + const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfDeclaration(node.typeParameter)); + const constraintType = getConstraintOfTypeParameter(typeParameter) || errorType; + const arrayOrTuple = !node.nameType && getArrayOrTupleOriginIndexType(constraintType); + if (arrayOrTuple) { + if (!node.type) { + return errorType; + } + const modifiers = getMappedTypeNodeModifiers(node); + const templateType = addOptionality(getTypeFromTypeNode(node.type), /*isProperty*/ true, !!(modifiers & MappedTypeModifiers.IncludeOptional)); + + if (isTupleType(arrayOrTuple)) { + return links.resolvedType = instantiateMappedTupleType( + arrayOrTuple, + modifiers, + typeParameter, + templateType, + /*mapper*/ undefined, + ); + } + + return links.resolvedType = instantiateMappedArrayType( + arrayOrTuple, + modifiers, + typeParameter, + templateType, + /*mapper*/ undefined, + ); + } const type = createObjectType(ObjectFlags.Mapped, node.symbol) as MappedType; type.declaration = node; type.aliasSymbol = getAliasSymbolForTypeNode(node); type.aliasTypeArguments = getTypeArgumentsForAliasSymbol(type.aliasSymbol); + type.typeParameter = typeParameter; + type.constraintType = constraintType; links.resolvedType = type; - // Eagerly resolve the constraint type which forces an error if the constraint type circularly - // references itself through one or more type aliases. - getConstraintTypeFromMappedType(type); } return links.resolvedType; } @@ -19310,13 +19367,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { isArrayType(t) || t.flags & TypeFlags.Any && findResolutionCycleStartIndex(typeVariable, TypeSystemPropertyName.ImmediateBaseConstraint) < 0 && (constraint = getConstraintOfTypeParameter(typeVariable)) && everyType(constraint, isArrayOrTupleType) ) { - return instantiateMappedArrayType(t, type, prependTypeMapping(typeVariable, t, mapper)); + return instantiateMappedArrayType(t, getMappedTypeModifiers(type), getTypeParameterFromMappedType(type), getTemplateTypeFromMappedType(type.target as MappedType || type), prependTypeMapping(typeVariable, t, mapper)); } if (isGenericTupleType(t)) { return instantiateMappedGenericTupleType(t, type, typeVariable, mapper); } if (isTupleType(t)) { - return instantiateMappedTupleType(t, type, prependTypeMapping(typeVariable, t, mapper)); + return instantiateMappedTupleType(t, getMappedTypeModifiers(type), getTypeParameterFromMappedType(type), getTemplateTypeFromMappedType(type.target as MappedType || type), prependTypeMapping(typeVariable, t, mapper)); } } return instantiateAnonymousType(type, prependTypeMapping(typeVariable, t, mapper)); @@ -19358,16 +19415,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return createTupleType(elementTypes, map(elementTypes, _ => ElementFlags.Variadic), newReadonly); } - function instantiateMappedArrayType(arrayType: Type, mappedType: MappedType, mapper: TypeMapper) { - const elementType = instantiateMappedTypeTemplate(mappedType, numberType, /*isOptional*/ true, mapper); + function instantiateMappedArrayType(arrayType: Type, modifiers: MappedTypeModifiers, typeParameter: TypeParameter, templateType: Type, mapper: TypeMapper | undefined) { + const elementType = instantiateMappedTypeTemplate(modifiers, typeParameter, templateType, numberType, /*isOptional*/ true, mapper); return isErrorType(elementType) ? errorType : - createArrayType(elementType, getModifiedReadonlyState(isReadonlyArrayType(arrayType), getMappedTypeModifiers(mappedType))); + createArrayType(elementType, getModifiedReadonlyState(isReadonlyArrayType(arrayType), modifiers)); } - function instantiateMappedTupleType(tupleType: TupleTypeReference, mappedType: MappedType, mapper: TypeMapper) { + function instantiateMappedTupleType(tupleType: TupleTypeReference, modifiers: MappedTypeModifiers, typeParameter: TypeParameter, templateType: Type, mapper: TypeMapper | undefined) { const elementFlags = tupleType.target.elementFlags; - const elementTypes = map(getElementTypes(tupleType), (_, i) => instantiateMappedTypeTemplate(mappedType, getStringLiteralType("" + i), !!(elementFlags[i] & ElementFlags.Optional), mapper)); - const modifiers = getMappedTypeModifiers(mappedType); + const elementTypes = map(getElementTypes(tupleType), (_, i) => instantiateMappedTypeTemplate(modifiers, typeParameter, templateType, getStringLiteralType("" + i), !!(elementFlags[i] & ElementFlags.Optional), mapper)); const newTupleModifiers = modifiers & MappedTypeModifiers.IncludeOptional ? map(elementFlags, f => f & ElementFlags.Required ? ElementFlags.Optional : f) : modifiers & MappedTypeModifiers.ExcludeOptional ? map(elementFlags, f => f & ElementFlags.Optional ? ElementFlags.Required : f) : elementFlags; @@ -19376,10 +19432,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { createTupleType(elementTypes, newTupleModifiers, newReadonly, tupleType.target.labeledElementDeclarations); } - function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) { - const templateMapper = appendTypeMapping(mapper, getTypeParameterFromMappedType(type), key); - const propType = instantiateType(getTemplateTypeFromMappedType(type.target as MappedType || type), templateMapper); - const modifiers = getMappedTypeModifiers(type); + function instantiateMappedTypeTemplate(modifiers: MappedTypeModifiers, typeParameter: TypeParameter, templateType: Type, key: Type, isOptional: boolean, mapper: TypeMapper | undefined) { + const templateMapper = appendTypeMapping(mapper, typeParameter, key); + const propType = instantiateType(templateType, templateMapper); return strictNullChecks && modifiers & MappedTypeModifiers.IncludeOptional && !maybeTypeOfKind(propType, TypeFlags.Undefined | TypeFlags.Void) ? getOptionalType(propType, /*isProperty*/ true) : strictNullChecks && modifiers & MappedTypeModifiers.ExcludeOptional && isOptional ? getTypeWithFacts(propType, TypeFacts.NEUndefined) : propType; @@ -39616,6 +39671,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const type = getTypeFromMappedTypeNode(node) as MappedType; + if (!(getObjectFlags(type) & ObjectFlags.Mapped)) { + return; + } const nameType = getNameTypeFromMappedType(type); if (nameType) { checkTypeAssignableTo(nameType, keyofConstraintType, node.nameType); diff --git a/src/lib/es2015.symbol.wellknown.d.ts b/src/lib/es2015.symbol.wellknown.d.ts index cf6ccef52b89f..6212e12c0d647 100644 --- a/src/lib/es2015.symbol.wellknown.d.ts +++ b/src/lib/es2015.symbol.wellknown.d.ts @@ -77,7 +77,7 @@ interface Array { * when they will be absent when used in a 'with' statement. */ readonly [Symbol.unscopables]: { - [K in keyof any[]]?: boolean; + [K in keyof any[] as K]?: boolean; }; } @@ -87,7 +87,7 @@ interface ReadonlyArray { * when they will be absent when used in a 'with' statement. */ readonly [Symbol.unscopables]: { - [K in keyof readonly any[]]?: boolean; + [K in keyof readonly any[] as K]?: boolean; }; } diff --git a/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.errors.txt b/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.errors.txt new file mode 100644 index 0000000000000..ac1457c07d724 --- /dev/null +++ b/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.errors.txt @@ -0,0 +1,49 @@ +mappedTypeConcreteTupleHomomorphism.ts(27,47): error TS2322: Type 'TupleOfNumbersAndObjects[K]' is not assignable to type 'string | number | bigint | boolean'. + Type '{} | 2 | 1' is not assignable to type 'string | number | bigint | boolean'. + Type '{}' is not assignable to type 'string | number | bigint | boolean'. + + +==== mappedTypeConcreteTupleHomomorphism.ts (1 errors) ==== + type TupleOfNumbers = [1, 2] + + type HomomorphicType = { + [K in keyof TupleOfNumbers]: `${TupleOfNumbers[K]}` + } + const homomorphic: HomomorphicType = ['1', '2'] + + type TupleOfNumbersKeys = keyof TupleOfNumbers + type HomomorphicType2 = { + [K in TupleOfNumbersKeys]: `${TupleOfNumbers[K]}` + } + const homomorphic2: HomomorphicType2 = ['1', '2'] + + type GenericType = { + [K in keyof T]: [K, T[K]] + } + + type HomomorphicInstantiation = { + [K in keyof GenericType<['c', 'd', 'e']>]: 1 + } + + const d: HomomorphicInstantiation = [1, 1, 1] + + type TupleOfNumbersAndObjects = [1, 2, {}] + + type ShouldErrorOnInterpolation = { + [K in keyof TupleOfNumbersAndObjects]: `${TupleOfNumbersAndObjects[K]}` + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'TupleOfNumbersAndObjects[K]' is not assignable to type 'string | number | bigint | boolean'. +!!! error TS2322: Type '{} | 2 | 1' is not assignable to type 'string | number | bigint | boolean'. +!!! error TS2322: Type '{}' is not assignable to type 'string | number | bigint | boolean'. + } + + // repro from #27995 + type Foo = ['a', 'b']; + + interface Bar { + a: string; + b: number; + } + + type Baz = { [K in keyof Foo]: Bar[Foo[K]]; }; + \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.js b/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.js new file mode 100644 index 0000000000000..f2a8b89a4a260 --- /dev/null +++ b/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.js @@ -0,0 +1,47 @@ +//// [tests/cases/compiler/mappedTypeConcreteTupleHomomorphism.ts] //// + +//// [mappedTypeConcreteTupleHomomorphism.ts] +type TupleOfNumbers = [1, 2] + +type HomomorphicType = { + [K in keyof TupleOfNumbers]: `${TupleOfNumbers[K]}` +} +const homomorphic: HomomorphicType = ['1', '2'] + +type TupleOfNumbersKeys = keyof TupleOfNumbers +type HomomorphicType2 = { + [K in TupleOfNumbersKeys]: `${TupleOfNumbers[K]}` +} +const homomorphic2: HomomorphicType2 = ['1', '2'] + +type GenericType = { + [K in keyof T]: [K, T[K]] +} + +type HomomorphicInstantiation = { + [K in keyof GenericType<['c', 'd', 'e']>]: 1 +} + +const d: HomomorphicInstantiation = [1, 1, 1] + +type TupleOfNumbersAndObjects = [1, 2, {}] + +type ShouldErrorOnInterpolation = { + [K in keyof TupleOfNumbersAndObjects]: `${TupleOfNumbersAndObjects[K]}` +} + +// repro from #27995 +type Foo = ['a', 'b']; + +interface Bar { + a: string; + b: number; +} + +type Baz = { [K in keyof Foo]: Bar[Foo[K]]; }; + + +//// [mappedTypeConcreteTupleHomomorphism.js] +var homomorphic = ['1', '2']; +var homomorphic2 = ['1', '2']; +var d = [1, 1, 1]; diff --git a/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.symbols b/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.symbols new file mode 100644 index 0000000000000..1f906e384d1c6 --- /dev/null +++ b/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.symbols @@ -0,0 +1,95 @@ +//// [tests/cases/compiler/mappedTypeConcreteTupleHomomorphism.ts] //// + +=== mappedTypeConcreteTupleHomomorphism.ts === +type TupleOfNumbers = [1, 2] +>TupleOfNumbers : Symbol(TupleOfNumbers, Decl(mappedTypeConcreteTupleHomomorphism.ts, 0, 0)) + +type HomomorphicType = { +>HomomorphicType : Symbol(HomomorphicType, Decl(mappedTypeConcreteTupleHomomorphism.ts, 0, 28)) + + [K in keyof TupleOfNumbers]: `${TupleOfNumbers[K]}` +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 3, 5)) +>TupleOfNumbers : Symbol(TupleOfNumbers, Decl(mappedTypeConcreteTupleHomomorphism.ts, 0, 0)) +>TupleOfNumbers : Symbol(TupleOfNumbers, Decl(mappedTypeConcreteTupleHomomorphism.ts, 0, 0)) +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 3, 5)) +} +const homomorphic: HomomorphicType = ['1', '2'] +>homomorphic : Symbol(homomorphic, Decl(mappedTypeConcreteTupleHomomorphism.ts, 5, 5)) +>HomomorphicType : Symbol(HomomorphicType, Decl(mappedTypeConcreteTupleHomomorphism.ts, 0, 28)) + +type TupleOfNumbersKeys = keyof TupleOfNumbers +>TupleOfNumbersKeys : Symbol(TupleOfNumbersKeys, Decl(mappedTypeConcreteTupleHomomorphism.ts, 5, 47)) +>TupleOfNumbers : Symbol(TupleOfNumbers, Decl(mappedTypeConcreteTupleHomomorphism.ts, 0, 0)) + +type HomomorphicType2 = { +>HomomorphicType2 : Symbol(HomomorphicType2, Decl(mappedTypeConcreteTupleHomomorphism.ts, 7, 46)) + + [K in TupleOfNumbersKeys]: `${TupleOfNumbers[K]}` +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 9, 5)) +>TupleOfNumbersKeys : Symbol(TupleOfNumbersKeys, Decl(mappedTypeConcreteTupleHomomorphism.ts, 5, 47)) +>TupleOfNumbers : Symbol(TupleOfNumbers, Decl(mappedTypeConcreteTupleHomomorphism.ts, 0, 0)) +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 9, 5)) +} +const homomorphic2: HomomorphicType2 = ['1', '2'] +>homomorphic2 : Symbol(homomorphic2, Decl(mappedTypeConcreteTupleHomomorphism.ts, 11, 5)) +>HomomorphicType2 : Symbol(HomomorphicType2, Decl(mappedTypeConcreteTupleHomomorphism.ts, 7, 46)) + +type GenericType = { +>GenericType : Symbol(GenericType, Decl(mappedTypeConcreteTupleHomomorphism.ts, 11, 49)) +>T : Symbol(T, Decl(mappedTypeConcreteTupleHomomorphism.ts, 13, 17)) + + [K in keyof T]: [K, T[K]] +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 14, 5)) +>T : Symbol(T, Decl(mappedTypeConcreteTupleHomomorphism.ts, 13, 17)) +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 14, 5)) +>T : Symbol(T, Decl(mappedTypeConcreteTupleHomomorphism.ts, 13, 17)) +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 14, 5)) +} + +type HomomorphicInstantiation = { +>HomomorphicInstantiation : Symbol(HomomorphicInstantiation, Decl(mappedTypeConcreteTupleHomomorphism.ts, 15, 1)) + + [K in keyof GenericType<['c', 'd', 'e']>]: 1 +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 18, 5)) +>GenericType : Symbol(GenericType, Decl(mappedTypeConcreteTupleHomomorphism.ts, 11, 49)) +} + +const d: HomomorphicInstantiation = [1, 1, 1] +>d : Symbol(d, Decl(mappedTypeConcreteTupleHomomorphism.ts, 21, 5)) +>HomomorphicInstantiation : Symbol(HomomorphicInstantiation, Decl(mappedTypeConcreteTupleHomomorphism.ts, 15, 1)) + +type TupleOfNumbersAndObjects = [1, 2, {}] +>TupleOfNumbersAndObjects : Symbol(TupleOfNumbersAndObjects, Decl(mappedTypeConcreteTupleHomomorphism.ts, 21, 45)) + +type ShouldErrorOnInterpolation = { +>ShouldErrorOnInterpolation : Symbol(ShouldErrorOnInterpolation, Decl(mappedTypeConcreteTupleHomomorphism.ts, 23, 42)) + + [K in keyof TupleOfNumbersAndObjects]: `${TupleOfNumbersAndObjects[K]}` +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 26, 5)) +>TupleOfNumbersAndObjects : Symbol(TupleOfNumbersAndObjects, Decl(mappedTypeConcreteTupleHomomorphism.ts, 21, 45)) +>TupleOfNumbersAndObjects : Symbol(TupleOfNumbersAndObjects, Decl(mappedTypeConcreteTupleHomomorphism.ts, 21, 45)) +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 26, 5)) +} + +// repro from #27995 +type Foo = ['a', 'b']; +>Foo : Symbol(Foo, Decl(mappedTypeConcreteTupleHomomorphism.ts, 27, 1)) + +interface Bar { +>Bar : Symbol(Bar, Decl(mappedTypeConcreteTupleHomomorphism.ts, 30, 22)) + + a: string; +>a : Symbol(Bar.a, Decl(mappedTypeConcreteTupleHomomorphism.ts, 32, 15)) + + b: number; +>b : Symbol(Bar.b, Decl(mappedTypeConcreteTupleHomomorphism.ts, 33, 14)) +} + +type Baz = { [K in keyof Foo]: Bar[Foo[K]]; }; +>Baz : Symbol(Baz, Decl(mappedTypeConcreteTupleHomomorphism.ts, 35, 1)) +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 37, 14)) +>Foo : Symbol(Foo, Decl(mappedTypeConcreteTupleHomomorphism.ts, 27, 1)) +>Bar : Symbol(Bar, Decl(mappedTypeConcreteTupleHomomorphism.ts, 30, 22)) +>Foo : Symbol(Foo, Decl(mappedTypeConcreteTupleHomomorphism.ts, 27, 1)) +>K : Symbol(K, Decl(mappedTypeConcreteTupleHomomorphism.ts, 37, 14)) + diff --git a/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.types b/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.types new file mode 100644 index 0000000000000..7be536633f665 --- /dev/null +++ b/tests/baselines/reference/mappedTypeConcreteTupleHomomorphism.types @@ -0,0 +1,74 @@ +//// [tests/cases/compiler/mappedTypeConcreteTupleHomomorphism.ts] //// + +=== mappedTypeConcreteTupleHomomorphism.ts === +type TupleOfNumbers = [1, 2] +>TupleOfNumbers : [1, 2] + +type HomomorphicType = { +>HomomorphicType : ["1", "2"] + + [K in keyof TupleOfNumbers]: `${TupleOfNumbers[K]}` +} +const homomorphic: HomomorphicType = ['1', '2'] +>homomorphic : ["1", "2"] +>['1', '2'] : ["1", "2"] +>'1' : "1" +>'2' : "2" + +type TupleOfNumbersKeys = keyof TupleOfNumbers +>TupleOfNumbersKeys : keyof TupleOfNumbers + +type HomomorphicType2 = { +>HomomorphicType2 : ["1", "2"] + + [K in TupleOfNumbersKeys]: `${TupleOfNumbers[K]}` +} +const homomorphic2: HomomorphicType2 = ['1', '2'] +>homomorphic2 : ["1", "2"] +>['1', '2'] : ["1", "2"] +>'1' : "1" +>'2' : "2" + +type GenericType = { +>GenericType : GenericType + + [K in keyof T]: [K, T[K]] +} + +type HomomorphicInstantiation = { +>HomomorphicInstantiation : [1, 1, 1] + + [K in keyof GenericType<['c', 'd', 'e']>]: 1 +} + +const d: HomomorphicInstantiation = [1, 1, 1] +>d : [1, 1, 1] +>[1, 1, 1] : [1, 1, 1] +>1 : 1 +>1 : 1 +>1 : 1 + +type TupleOfNumbersAndObjects = [1, 2, {}] +>TupleOfNumbersAndObjects : [1, 2, {}] + +type ShouldErrorOnInterpolation = { +>ShouldErrorOnInterpolation : ["1", "2", string] + + [K in keyof TupleOfNumbersAndObjects]: `${TupleOfNumbersAndObjects[K]}` +} + +// repro from #27995 +type Foo = ['a', 'b']; +>Foo : ["a", "b"] + +interface Bar { + a: string; +>a : string + + b: number; +>b : number +} + +type Baz = { [K in keyof Foo]: Bar[Foo[K]]; }; +>Baz : [string, number] + diff --git a/tests/cases/compiler/mappedTypeConcreteTupleHomomorphism.ts b/tests/cases/compiler/mappedTypeConcreteTupleHomomorphism.ts new file mode 100644 index 0000000000000..843e20436d6d5 --- /dev/null +++ b/tests/cases/compiler/mappedTypeConcreteTupleHomomorphism.ts @@ -0,0 +1,38 @@ +type TupleOfNumbers = [1, 2] + +type HomomorphicType = { + [K in keyof TupleOfNumbers]: `${TupleOfNumbers[K]}` +} +const homomorphic: HomomorphicType = ['1', '2'] + +type TupleOfNumbersKeys = keyof TupleOfNumbers +type HomomorphicType2 = { + [K in TupleOfNumbersKeys]: `${TupleOfNumbers[K]}` +} +const homomorphic2: HomomorphicType2 = ['1', '2'] + +type GenericType = { + [K in keyof T]: [K, T[K]] +} + +type HomomorphicInstantiation = { + [K in keyof GenericType<['c', 'd', 'e']>]: 1 +} + +const d: HomomorphicInstantiation = [1, 1, 1] + +type TupleOfNumbersAndObjects = [1, 2, {}] + +type ShouldErrorOnInterpolation = { + [K in keyof TupleOfNumbersAndObjects]: `${TupleOfNumbersAndObjects[K]}` +} + +// repro from #27995 +type Foo = ['a', 'b']; + +interface Bar { + a: string; + b: number; +} + +type Baz = { [K in keyof Foo]: Bar[Foo[K]]; };