mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-06 02:33:53 -06:00
Add support for numbers and symbols in keyof (but keep it disabled)
This commit is contained in:
parent
ccf20d3f67
commit
ff20f38405
@ -72,6 +72,7 @@ namespace ts {
|
||||
const strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization");
|
||||
const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");
|
||||
const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
|
||||
const keyofStringsOnly = true; // !!compilerOptions.keyofStringsOnly;
|
||||
|
||||
const emitResolver = createResolver();
|
||||
const nodeBuilder = createNodeBuilder();
|
||||
@ -346,6 +347,9 @@ namespace ts {
|
||||
const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
|
||||
const implicitNeverType = createIntrinsicType(TypeFlags.Never, "never");
|
||||
const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
|
||||
const stringNumberType = getUnionType([stringType, numberType]);
|
||||
const stringNumberSymbolType = getUnionType([stringType, numberType, esSymbolType]);
|
||||
const keyofConstraintType = keyofStringsOnly ? stringType : stringNumberSymbolType;
|
||||
|
||||
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
|
||||
|
||||
@ -420,6 +424,7 @@ namespace ts {
|
||||
let deferredGlobalAsyncIteratorType: GenericType;
|
||||
let deferredGlobalAsyncIterableIteratorType: GenericType;
|
||||
let deferredGlobalTemplateStringsArrayType: ObjectType;
|
||||
let deferredGlobalExtractSymbol: Symbol;
|
||||
|
||||
let deferredNodes: Node[];
|
||||
let deferredUnusedIdentifierNodes: Node[];
|
||||
@ -4207,7 +4212,7 @@ namespace ts {
|
||||
// right hand expression is of a type parameter type.
|
||||
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
|
||||
const indexType = getIndexType(checkNonNullExpression(declaration.parent.parent.expression));
|
||||
return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? indexType : stringType;
|
||||
return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? getExtractStringType(indexType) : stringType;
|
||||
}
|
||||
|
||||
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {
|
||||
@ -6015,6 +6020,7 @@ namespace ts {
|
||||
function resolveMappedTypeMembers(type: MappedType) {
|
||||
const members: SymbolTable = createSymbolTable();
|
||||
let stringIndexInfo: IndexInfo;
|
||||
let numberIndexInfo: IndexInfo;
|
||||
// Resolve upfront such that recursive references see an empty object type.
|
||||
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,
|
||||
@ -6025,15 +6031,19 @@ namespace ts {
|
||||
const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
|
||||
const templateModifiers = getMappedTypeModifiers(type);
|
||||
const constraintDeclaration = type.declaration.typeParameter.constraint;
|
||||
const include = keyofStringsOnly ? TypeFlags.StringLiteral : TypeFlags.StringOrNumberLiteralOrUnique;
|
||||
if (constraintDeclaration.kind === SyntaxKind.TypeOperator &&
|
||||
(<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
|
||||
// We have a { [P in keyof T]: X }
|
||||
for (const prop of getPropertiesOfType(modifiersType)) {
|
||||
addMemberForKeyType(getLiteralTypeFromPropertyName(prop), undefined, prop);
|
||||
addMemberForKeyType(getLiteralTypeFromPropertyName(prop, include), undefined, prop);
|
||||
}
|
||||
if (modifiersType.flags & TypeFlags.Any || getIndexInfoOfType(modifiersType, IndexKind.String)) {
|
||||
addMemberForKeyType(stringType);
|
||||
}
|
||||
if (!keyofStringsOnly && getIndexInfoOfType(modifiersType, IndexKind.Number)) {
|
||||
addMemberForKeyType(numberType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// First, if the constraint type is a type parameter, obtain the base constraint. Then,
|
||||
@ -6043,7 +6053,7 @@ namespace ts {
|
||||
const iterationType = keyType.flags & TypeFlags.Index ? getIndexType(getApparentType((<IndexType>keyType).type)) : keyType;
|
||||
forEachType(iterationType, addMemberForKeyType);
|
||||
}
|
||||
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, undefined);
|
||||
setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
|
||||
|
||||
function addMemberForKeyType(t: Type, _index?: number, origin?: Symbol) {
|
||||
// Create a mapper from T to the current iteration type constituent. Then, if the
|
||||
@ -6077,6 +6087,9 @@ namespace ts {
|
||||
else if (t.flags & (TypeFlags.Any | TypeFlags.String)) {
|
||||
stringIndexInfo = createIndexInfo(propType, !!(templateModifiers & MappedTypeModifiers.IncludeReadonly));
|
||||
}
|
||||
else if (t.flags & TypeFlags.Number) {
|
||||
numberIndexInfo = createIndexInfo(propType, !!(templateModifiers & MappedTypeModifiers.IncludeReadonly));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6468,6 +6481,7 @@ namespace ts {
|
||||
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
|
||||
t.flags & TypeFlags.ESSymbolLike ? getGlobalESSymbolType(/*reportErrors*/ languageVersion >= ScriptTarget.ES2015) :
|
||||
t.flags & TypeFlags.NonPrimitive ? emptyObjectType :
|
||||
t.flags & TypeFlags.Index ? keyofConstraintType :
|
||||
t;
|
||||
}
|
||||
|
||||
@ -7665,6 +7679,10 @@ namespace ts {
|
||||
return symbol && <GenericType>getTypeOfGlobalSymbol(symbol, arity);
|
||||
}
|
||||
|
||||
function getGlobalExtractSymbol(): Symbol {
|
||||
return deferredGlobalExtractSymbol || (deferredGlobalExtractSymbol = getGlobalSymbol("Extract" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a global type that is generic with some element type, and returns that instantiation.
|
||||
*/
|
||||
@ -8107,11 +8125,11 @@ namespace ts {
|
||||
return type.resolvedIndexType;
|
||||
}
|
||||
|
||||
function getLiteralTypeFromPropertyName(prop: Symbol) {
|
||||
function getLiteralTypeFromPropertyName(prop: Symbol, include: TypeFlags) {
|
||||
if (!(getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier)) {
|
||||
const nameType = getLateBoundSymbol(prop).nameType;
|
||||
if (nameType) {
|
||||
return nameType.flags & TypeFlags.StringLiteral ? nameType : neverType;
|
||||
return nameType.flags & include ? nameType : neverType;
|
||||
}
|
||||
if (!isKnownSymbol(prop)) {
|
||||
return getLiteralType(symbolName(prop));
|
||||
@ -8120,8 +8138,25 @@ namespace ts {
|
||||
return neverType;
|
||||
}
|
||||
|
||||
function getLiteralTypeFromPropertyNames(type: Type) {
|
||||
return getUnionType(map(getPropertiesOfType(type), getLiteralTypeFromPropertyName));
|
||||
function getLiteralTypeFromPropertyNames(type: Type, include: TypeFlags) {
|
||||
return getUnionType(map(getPropertiesOfType(type), t => getLiteralTypeFromPropertyName(t, include)));
|
||||
}
|
||||
|
||||
function getNonEnumNumberIndexInfo(type: Type) {
|
||||
const numberIndexInfo = getIndexInfoOfType(type, IndexKind.Number);
|
||||
return numberIndexInfo !== enumNumberIndexInfo ? numberIndexInfo : undefined;
|
||||
}
|
||||
|
||||
function getStringOnlyIndexType(type: Type) {
|
||||
return type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType :
|
||||
getLiteralTypeFromPropertyNames(type, TypeFlags.StringLiteral);
|
||||
}
|
||||
|
||||
function getStringNumberSymbolIndexType(type: Type) {
|
||||
return type.flags & TypeFlags.Any ? stringNumberSymbolType :
|
||||
getIndexInfoOfType(type, IndexKind.String) ? getUnionType([stringNumberType, getLiteralTypeFromPropertyNames(type, TypeFlags.UniqueESSymbol)]) :
|
||||
getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromPropertyNames(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol)]) :
|
||||
getLiteralTypeFromPropertyNames(type, TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.UniqueESSymbol);
|
||||
}
|
||||
|
||||
function getIndexType(type: Type): Type {
|
||||
@ -8129,12 +8164,19 @@ namespace ts {
|
||||
maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(<InstantiableType | UnionOrIntersectionType>type) :
|
||||
getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(<MappedType>type) :
|
||||
type === wildcardType ? wildcardType :
|
||||
type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType :
|
||||
getLiteralTypeFromPropertyNames(type);
|
||||
keyofStringsOnly ? getStringOnlyIndexType(type) : getStringNumberSymbolIndexType(type);
|
||||
}
|
||||
|
||||
function getExtractStringType(type: Type) {
|
||||
if (keyofStringsOnly) {
|
||||
return type;
|
||||
}
|
||||
const extractTypeAlias = getGlobalExtractSymbol();
|
||||
return extractTypeAlias ? getTypeAliasInstantiation(extractTypeAlias, [type, stringType]) : stringType;
|
||||
}
|
||||
|
||||
function getIndexTypeOrString(type: Type): Type {
|
||||
const indexType = getIndexType(type);
|
||||
const indexType = getExtractStringType(getIndexType(type));
|
||||
return indexType.flags & TypeFlags.Never ? stringType : indexType;
|
||||
}
|
||||
|
||||
@ -8632,7 +8674,7 @@ namespace ts {
|
||||
if (right.flags & TypeFlags.Union) {
|
||||
return mapType(right, t => getSpreadType(left, t, symbol, typeFlags, objectFlags));
|
||||
}
|
||||
if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive)) {
|
||||
if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index)) {
|
||||
return left;
|
||||
}
|
||||
|
||||
@ -10379,6 +10421,12 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (source.flags & TypeFlags.Index) {
|
||||
if (result = isRelatedTo(keyofConstraintType, target, reportErrors)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else if (source.flags & TypeFlags.Conditional) {
|
||||
if (target.flags & TypeFlags.Conditional) {
|
||||
// Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if
|
||||
@ -15190,7 +15238,7 @@ namespace ts {
|
||||
// type, and any union of these types (like string | number).
|
||||
if (links.resolvedType.flags & TypeFlags.Nullable ||
|
||||
!isTypeAssignableToKind(links.resolvedType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) &&
|
||||
!isTypeAssignableTo(links.resolvedType, getUnionType([stringType, numberType, esSymbolType]))) {
|
||||
!isTypeAssignableTo(links.resolvedType, stringNumberSymbolType)) {
|
||||
error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any);
|
||||
}
|
||||
else {
|
||||
@ -20841,7 +20889,7 @@ namespace ts {
|
||||
|
||||
const type = <MappedType>getTypeFromMappedTypeNode(node);
|
||||
const constraintType = getConstraintTypeFromMappedType(type);
|
||||
checkTypeAssignableTo(constraintType, stringType, node.typeParameter.constraint);
|
||||
checkTypeAssignableTo(constraintType, keyofConstraintType, node.typeParameter.constraint);
|
||||
}
|
||||
|
||||
function checkTypeOperator(node: TypeOperatorNode) {
|
||||
|
||||
@ -678,6 +678,12 @@ namespace ts {
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Disable_strict_checking_of_generic_signatures_in_function_types,
|
||||
},
|
||||
{
|
||||
name: "keyofStringsOnly",
|
||||
type: "boolean",
|
||||
category: Diagnostics.Advanced_Options,
|
||||
description: Diagnostics.Resolve_keyof_to_string_valued_property_names_only_no_numbers_or_symbols,
|
||||
},
|
||||
{
|
||||
// A list of plugins to load in the language service
|
||||
name: "plugins",
|
||||
|
||||
@ -3516,6 +3516,10 @@
|
||||
"category": "Error",
|
||||
"code": 6192
|
||||
},
|
||||
"Resolve 'keyof' to string valued property names only (no numbers or symbols).": {
|
||||
"category": "Message",
|
||||
"code": 6193
|
||||
},
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
"code": 7005
|
||||
|
||||
@ -3596,7 +3596,7 @@ namespace ts {
|
||||
Intrinsic = Any | String | Number | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
|
||||
/* @internal */
|
||||
Primitive = String | Number | Boolean | Enum | EnumLiteral | ESSymbol | Void | Undefined | Null | Literal | UniqueESSymbol,
|
||||
StringLike = String | StringLiteral | Index,
|
||||
StringLike = String | StringLiteral,
|
||||
NumberLike = Number | NumberLiteral | Enum,
|
||||
BooleanLike = Boolean | BooleanLiteral,
|
||||
EnumLike = Enum | EnumLiteral,
|
||||
@ -4144,6 +4144,7 @@ namespace ts {
|
||||
inlineSources?: boolean;
|
||||
isolatedModules?: boolean;
|
||||
jsx?: JsxEmit;
|
||||
keyofStringsOnly?: boolean;
|
||||
lib?: string[];
|
||||
/*@internal*/listEmittedFiles?: boolean;
|
||||
/*@internal*/listFiles?: boolean;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user