@@ -72,6 +72,7 @@ namespace ts {
7272 const strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization");
7373 const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");
7474 const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
75+ const keyofStringsOnly = true; // !!compilerOptions.keyofStringsOnly;
7576
7677 const emitResolver = createResolver();
7778 const nodeBuilder = createNodeBuilder();
@@ -346,6 +347,9 @@ namespace ts {
346347 const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
347348 const implicitNeverType = createIntrinsicType(TypeFlags.Never, "never");
348349 const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
350+ const stringNumberType = getUnionType([stringType, numberType]);
351+ const stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]);
352+ const keyofConstraintType = keyofStringsOnly ? stringType : stringNumberSymbolType;
349353
350354 const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
351355
@@ -420,6 +424,7 @@ namespace ts {
420424 let deferredGlobalAsyncIteratorType: GenericType;
421425 let deferredGlobalAsyncIterableIteratorType: GenericType;
422426 let deferredGlobalTemplateStringsArrayType: ObjectType;
427+ let deferredGlobalExtractSymbol: Symbol;
423428
424429 let deferredNodes: Node[];
425430 let deferredUnusedIdentifierNodes: Node[];
@@ -4207,7 +4212,7 @@ namespace ts {
42074212 // right hand expression is of a type parameter type.
42084213 if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
42094214 const indexType = getIndexType(checkNonNullExpression(declaration.parent.parent.expression));
4210- return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? indexType : stringType;
4215+ return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? getExtractStringType( indexType) : stringType;
42114216 }
42124217
42134218 if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
@@ -6015,6 +6020,7 @@ namespace ts {
60156020 function resolveMappedTypeMembers(type: MappedType) {
60166021 const members: SymbolTable = createSymbolTable();
60176022 let stringIndexInfo: IndexInfo;
6023+ let numberIndexInfo: IndexInfo;
60186024 // Resolve upfront such that recursive references see an empty object type.
60196025 setStructuredTypeMembers(type, emptySymbols, emptyArray, emptyArray, undefined, undefined);
60206026 // In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type,
@@ -6025,15 +6031,19 @@ namespace ts {
60256031 const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
60266032 const templateModifiers = getMappedTypeModifiers(type);
60276033 const constraintDeclaration = type.declaration.typeParameter.constraint;
6034+ const include = keyofStringsOnly ? TypeFlags.StringLiteral : TypeFlags.StringOrNumberLiteralOrUnique;
60286035 if (constraintDeclaration.kind === SyntaxKind.TypeOperator &&
60296036 (<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
60306037 // We have a { [P in keyof T]: X }
60316038 for (const prop of getPropertiesOfType(modifiersType)) {
6032- addMemberForKeyType(getLiteralTypeFromPropertyName(prop), undefined, prop);
6039+ addMemberForKeyType(getLiteralTypeFromPropertyName(prop, include ), undefined, prop);
60336040 }
60346041 if (modifiersType.flags & TypeFlags.Any || getIndexInfoOfType(modifiersType, IndexKind.String)) {
60356042 addMemberForKeyType(stringType);
60366043 }
6044+ if (!keyofStringsOnly && getIndexInfoOfType(modifiersType, IndexKind.Number)) {
6045+ addMemberForKeyType(numberType);
6046+ }
60376047 }
60386048 else {
60396049 // First, if the constraint type is a type parameter, obtain the base constraint. Then,
@@ -6043,7 +6053,7 @@ namespace ts {
60436053 const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>keyType).type)) : keyType;
60446054 forEachType(iterationType, addMemberForKeyType);
60456055 }
6046- setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined );
6056+ setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo );
60476057
60486058 function addMemberForKeyType(t: Type, _index?: number, origin?: Symbol) {
60496059 // Create a mapper from T to the current iteration type constituent. Then, if the
@@ -6077,6 +6087,9 @@ namespace ts {
60776087 else if (t.flags & (TypeFlags.Any | TypeFlags.String)) {
60786088 stringIndexInfo = createIndexInfo(propType, !!(templateModifiers & MappedTypeModifiers.IncludeReadonly));
60796089 }
6090+ else if (t.flags & TypeFlags.Number) {
6091+ numberIndexInfo = createIndexInfo(propType, !!(templateModifiers & MappedTypeModifiers.IncludeReadonly));
6092+ }
60806093 }
60816094 }
60826095
@@ -6468,6 +6481,7 @@ namespace ts {
64686481 t.flags & TypeFlags.BooleanLike ? globalBooleanType :
64696482 t.flags & TypeFlags.ESSymbolLike ? getGlobalESSymbolType(/*reportErrors*/ languageVersion >= ScriptTarget.ES2015) :
64706483 t.flags & TypeFlags.NonPrimitive ? emptyObjectType :
6484+ t.flags & TypeFlags.Index ? keyofConstraintType :
64716485 t;
64726486 }
64736487
@@ -7665,6 +7679,10 @@ namespace ts {
76657679 return symbol && <GenericType>getTypeOfGlobalSymbol(symbol, arity);
76667680 }
76677681
7682+ function getGlobalExtractSymbol(): Symbol {
7683+ return deferredGlobalExtractSymbol || (deferredGlobalExtractSymbol = getGlobalSymbol("Extract" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0));
7684+ }
7685+
76687686 /**
76697687 * Instantiates a global type that is generic with some element type, and returns that instantiation.
76707688 */
@@ -8107,11 +8125,11 @@ namespace ts {
81078125 return type.resolvedIndexType;
81088126 }
81098127
8110- function getLiteralTypeFromPropertyName(prop: Symbol) {
8128+ function getLiteralTypeFromPropertyName(prop: Symbol, include: TypeFlags ) {
81118129 if (!(getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier)) {
81128130 const nameType = getLateBoundSymbol(prop).nameType;
81138131 if (nameType) {
8114- return nameType.flags & TypeFlags.StringLiteral ? nameType : neverType;
8132+ return nameType.flags & include ? nameType : neverType;
81158133 }
81168134 if (!isKnownSymbol(prop)) {
81178135 return getLiteralType(symbolName(prop));
@@ -8120,21 +8138,45 @@ namespace ts {
81208138 return neverType;
81218139 }
81228140
8123- function getLiteralTypeFromPropertyNames(type: Type) {
8124- return getUnionType(map(getPropertiesOfType(type), getLiteralTypeFromPropertyName));
8141+ function getLiteralTypeFromPropertyNames(type: Type, include: TypeFlags) {
8142+ return getUnionType(map(getPropertiesOfType(type), t => getLiteralTypeFromPropertyName(t, include)));
8143+ }
8144+
8145+ function getNonEnumNumberIndexInfo(type: Type) {
8146+ const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number);
8147+ return numberIndexInfo !== enumNumberIndexInfo ? numberIndexInfo : undefined;
8148+ }
8149+
8150+ function getStringOnlyIndexType(type: Type) {
8151+ return type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType :
8152+ getLiteralTypeFromPropertyNames(type, TypeFlags.StringLiteral);
8153+ }
8154+
8155+ function getStringNumberSymbolIndexType(type: Type) {
8156+ return type.flags & TypeFlags.Any ? stringNumberSymbolType :
8157+ getIndexInfoOfType(type, IndexKind.String) ? getUnionType([stringNumberType, getLiteralTypeFromPropertyNames(type, TypeFlags.UniqueESSymbol)]) :
8158+ getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromPropertyNames(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol)]) :
8159+ getLiteralTypeFromPropertyNames(type, TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.UniqueESSymbol);
81258160 }
81268161
81278162 function getIndexType(type: Type): Type {
81288163 return type.flags & TypeFlags.Intersection ? getUnionType(map((<IntersectionType>type).types, t => getIndexType(t))) :
81298164 maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(<InstantiableType | UnionOrIntersectionType>type) :
81308165 getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(<MappedType>type) :
81318166 type === wildcardType ? wildcardType :
8132- type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType :
8133- getLiteralTypeFromPropertyNames(type);
8167+ keyofStringsOnly ? getStringOnlyIndexType(type) : getStringNumberSymbolIndexType(type);
8168+ }
8169+
8170+ function getExtractStringType(type: Type) {
8171+ if (keyofStringsOnly) {
8172+ return type;
8173+ }
8174+ const extractTypeAlias = getGlobalExtractSymbol();
8175+ return extractTypeAlias ? getTypeAliasInstantiation(extractTypeAlias, [type, stringType]) : stringType;
81348176 }
81358177
81368178 function getIndexTypeOrString(type: Type): Type {
8137- const indexType = getIndexType(type);
8179+ const indexType = getExtractStringType( getIndexType(type) );
81388180 return indexType.flags & TypeFlags.Never ? stringType : indexType;
81398181 }
81408182
@@ -8632,7 +8674,7 @@ namespace ts {
86328674 if (right.flags & TypeFlags.Union) {
86338675 return mapType(right, t => getSpreadType(left, t, symbol, typeFlags, objectFlags));
86348676 }
8635- if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive)) {
8677+ if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index )) {
86368678 return left;
86378679 }
86388680
@@ -10379,6 +10421,12 @@ namespace ts {
1037910421 }
1038010422 }
1038110423 }
10424+ else if (source.flags & TypeFlags.Index) {
10425+ if (result = isRelatedTo(keyofConstraintType, target, reportErrors)) {
10426+ errorInfo = saveErrorInfo;
10427+ return result;
10428+ }
10429+ }
1038210430 else if (source.flags & TypeFlags.Conditional) {
1038310431 if (target.flags & TypeFlags.Conditional) {
1038410432 // Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if
@@ -15190,7 +15238,7 @@ namespace ts {
1519015238 // type, and any union of these types (like string | number).
1519115239 if (links.resolvedType.flags & TypeFlags.Nullable ||
1519215240 !isTypeAssignableToKind(links.resolvedType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) &&
15193- !isTypeAssignableTo(links.resolvedType, getUnionType([stringType, numberType, esSymbolType]) )) {
15241+ !isTypeAssignableTo(links.resolvedType, stringNumberSymbolType )) {
1519415242 error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any);
1519515243 }
1519615244 else {
@@ -20841,7 +20889,7 @@ namespace ts {
2084120889
2084220890 const type = <MappedType>getTypeFromMappedTypeNode(node);
2084320891 const constraintType = getConstraintTypeFromMappedType(type);
20844- checkTypeAssignableTo(constraintType, stringType , node.typeParameter.constraint);
20892+ checkTypeAssignableTo(constraintType, keyofConstraintType , node.typeParameter.constraint);
2084520893 }
2084620894
2084720895 function checkTypeOperator(node: TypeOperatorNode) {
0 commit comments