diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 748cab985dab1..32c8e0a59499f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2246,16 +2246,32 @@ namespace ts { } const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false)); if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) { - const message = isES2015OrLaterConstructorName(name) - ? Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later - : Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here; - error(errorLocation, message, unescapeLeadingUnderscores(name)); + const rawName = unescapeLeadingUnderscores(name); + if (isES2015OrLaterConstructorName(name)) { + error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later, rawName); + } + else if (maybeMappedType(errorLocation, symbol)) { + error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Did_you_mean_to_use_1_in_0, rawName, rawName === "K" ? "P" : "K"); + } + else { + error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, rawName); + } return true; } } return false; } + function maybeMappedType(node: Node, symbol: Symbol) { + const container = findAncestor(node.parent, n => + isComputedPropertyName(n) || isPropertySignature(n) ? false : isTypeLiteralNode(n) || "quit") as TypeLiteralNode | undefined; + if (container && container.members.length === 1) { + const type = getDeclaredTypeOfSymbol(symbol); + return !!(type.flags & TypeFlags.Union) && allTypesAssignableToKind(type, TypeFlags.StringOrNumberLiteral, /*strict*/ true); + } + return false; + } + function isES2015OrLaterConstructorName(n: __String) { switch (n) { case "Promise": diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 82f3cb4ce579b..b7d423bbc8211 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2607,6 +2607,10 @@ "category": "Error", "code": 2689 }, + "'{0}' only refers to a type, but is being used as a value here. Did you mean to use '{1} in {0}'?": { + "category": "Error", + "code": 2690 + }, "An import path cannot end with a '{0}' extension. Consider importing '{1}' instead.": { "category": "Error", "code": 2691 diff --git a/tests/baselines/reference/typeUsedAsTypeLiteralIndex.errors.txt b/tests/baselines/reference/typeUsedAsTypeLiteralIndex.errors.txt new file mode 100644 index 0000000000000..1a6accee7a157 --- /dev/null +++ b/tests/baselines/reference/typeUsedAsTypeLiteralIndex.errors.txt @@ -0,0 +1,53 @@ +tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts(3,5): error TS1170: A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. +tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts(3,6): error TS2690: 'K' only refers to a type, but is being used as a value here. Did you mean to use 'P in K'? +tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts(13,5): error TS1170: A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. +tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts(13,6): error TS2690: 'K2' only refers to a type, but is being used as a value here. Did you mean to use 'K in K2'? +tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts(18,5): error TS1170: A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. +tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts(18,6): error TS2690: 'K3' only refers to a type, but is being used as a value here. Did you mean to use 'K in K3'? +tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts(23,5): error TS1170: A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. +tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts(23,6): error TS2693: 'K4' only refers to a type, but is being used as a value here. + + +==== tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts (8 errors) ==== + type K = number | string; + type T = { + [K]: number; // Did you mean to use 'P in K'? + ~~~ +!!! error TS1170: A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. + ~ +!!! error TS2690: 'K' only refers to a type, but is being used as a value here. Did you mean to use 'P in K'? + } + + const K1 = Symbol(); + type T1 = { + [K1]: number; + } + + type K2 = "x" | "y"; + type T2 = { + [K2]: number; // Did you mean to use 'K in K2'? + ~~~~ +!!! error TS1170: A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. + ~~ +!!! error TS2690: 'K2' only refers to a type, but is being used as a value here. Did you mean to use 'K in K2'? + } + + type K3 = number | string; + type T3 = { + [K3]: number; // Did you mean to use 'K in K3'? + ~~~~ +!!! error TS1170: A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. + ~~ +!!! error TS2690: 'K3' only refers to a type, but is being used as a value here. Did you mean to use 'K in K3'? + } + + type K4 = number | string; + type T4 = { + [K4]: number; + ~~~~ +!!! error TS1170: A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type. + ~~ +!!! error TS2693: 'K4' only refers to a type, but is being used as a value here. + k4: string; + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeUsedAsTypeLiteralIndex.js b/tests/baselines/reference/typeUsedAsTypeLiteralIndex.js new file mode 100644 index 0000000000000..444dd328056a8 --- /dev/null +++ b/tests/baselines/reference/typeUsedAsTypeLiteralIndex.js @@ -0,0 +1,30 @@ +//// [typeUsedAsTypeLiteralIndex.ts] +type K = number | string; +type T = { + [K]: number; // Did you mean to use 'P in K'? +} + +const K1 = Symbol(); +type T1 = { + [K1]: number; +} + +type K2 = "x" | "y"; +type T2 = { + [K2]: number; // Did you mean to use 'K in K2'? +} + +type K3 = number | string; +type T3 = { + [K3]: number; // Did you mean to use 'K in K3'? +} + +type K4 = number | string; +type T4 = { + [K4]: number; + k4: string; +} + + +//// [typeUsedAsTypeLiteralIndex.js] +const K1 = Symbol(); diff --git a/tests/baselines/reference/typeUsedAsTypeLiteralIndex.symbols b/tests/baselines/reference/typeUsedAsTypeLiteralIndex.symbols new file mode 100644 index 0000000000000..840517048c513 --- /dev/null +++ b/tests/baselines/reference/typeUsedAsTypeLiteralIndex.symbols @@ -0,0 +1,56 @@ +=== tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts === +type K = number | string; +>K : Symbol(K, Decl(typeUsedAsTypeLiteralIndex.ts, 0, 0)) + +type T = { +>T : Symbol(T, Decl(typeUsedAsTypeLiteralIndex.ts, 0, 25)) + + [K]: number; // Did you mean to use 'P in K'? +>[K] : Symbol([K], Decl(typeUsedAsTypeLiteralIndex.ts, 1, 10)) +} + +const K1 = Symbol(); +>K1 : Symbol(K1, Decl(typeUsedAsTypeLiteralIndex.ts, 5, 5)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) + +type T1 = { +>T1 : Symbol(T1, Decl(typeUsedAsTypeLiteralIndex.ts, 5, 20)) + + [K1]: number; +>[K1] : Symbol([K1], Decl(typeUsedAsTypeLiteralIndex.ts, 6, 11)) +>K1 : Symbol(K1, Decl(typeUsedAsTypeLiteralIndex.ts, 5, 5)) +} + +type K2 = "x" | "y"; +>K2 : Symbol(K2, Decl(typeUsedAsTypeLiteralIndex.ts, 8, 1)) + +type T2 = { +>T2 : Symbol(T2, Decl(typeUsedAsTypeLiteralIndex.ts, 10, 20)) + + [K2]: number; // Did you mean to use 'K in K2'? +>[K2] : Symbol([K2], Decl(typeUsedAsTypeLiteralIndex.ts, 11, 11)) +} + +type K3 = number | string; +>K3 : Symbol(K3, Decl(typeUsedAsTypeLiteralIndex.ts, 13, 1)) + +type T3 = { +>T3 : Symbol(T3, Decl(typeUsedAsTypeLiteralIndex.ts, 15, 26)) + + [K3]: number; // Did you mean to use 'K in K3'? +>[K3] : Symbol([K3], Decl(typeUsedAsTypeLiteralIndex.ts, 16, 11)) +} + +type K4 = number | string; +>K4 : Symbol(K4, Decl(typeUsedAsTypeLiteralIndex.ts, 18, 1)) + +type T4 = { +>T4 : Symbol(T4, Decl(typeUsedAsTypeLiteralIndex.ts, 20, 26)) + + [K4]: number; +>[K4] : Symbol([K4], Decl(typeUsedAsTypeLiteralIndex.ts, 21, 11)) + + k4: string; +>k4 : Symbol(k4, Decl(typeUsedAsTypeLiteralIndex.ts, 22, 17)) +} + diff --git a/tests/baselines/reference/typeUsedAsTypeLiteralIndex.types b/tests/baselines/reference/typeUsedAsTypeLiteralIndex.types new file mode 100644 index 0000000000000..2b362f0d5b4d4 --- /dev/null +++ b/tests/baselines/reference/typeUsedAsTypeLiteralIndex.types @@ -0,0 +1,61 @@ +=== tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts === +type K = number | string; +>K : K + +type T = { +>T : T + + [K]: number; // Did you mean to use 'P in K'? +>[K] : number +>K : any +} + +const K1 = Symbol(); +>K1 : unique symbol +>Symbol() : unique symbol +>Symbol : SymbolConstructor + +type T1 = { +>T1 : T1 + + [K1]: number; +>[K1] : number +>K1 : unique symbol +} + +type K2 = "x" | "y"; +>K2 : K2 + +type T2 = { +>T2 : T2 + + [K2]: number; // Did you mean to use 'K in K2'? +>[K2] : number +>K2 : any +} + +type K3 = number | string; +>K3 : K + +type T3 = { +>T3 : T3 + + [K3]: number; // Did you mean to use 'K in K3'? +>[K3] : number +>K3 : any +} + +type K4 = number | string; +>K4 : K + +type T4 = { +>T4 : T4 + + [K4]: number; +>[K4] : number +>K4 : any + + k4: string; +>k4 : string +} + diff --git a/tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts b/tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts new file mode 100644 index 0000000000000..908491330e206 --- /dev/null +++ b/tests/cases/compiler/typeUsedAsTypeLiteralIndex.ts @@ -0,0 +1,27 @@ +// @target: esnext + +type K = number | string; +type T = { + [K]: number; // Did you mean to use 'P in K'? +} + +const K1 = Symbol(); +type T1 = { + [K1]: number; +} + +type K2 = "x" | "y"; +type T2 = { + [K2]: number; // Did you mean to use 'K in K2'? +} + +type K3 = number | string; +type T3 = { + [K3]: number; // Did you mean to use 'K in K3'? +} + +type K4 = number | string; +type T4 = { + [K4]: number; + k4: string; +}