Closed
Description
A "direct" string literal type defined using type Foo = "A";
behaves properly in more places than an apparently-equivalent "indirect" string literal type defined using mapped types.
TypeScript Version: 2.3.4
Code
I discovered this issue while using https://github.com/dphilipson/typescript-string-enums, and as such, have inlined the relevant part of the library into the top of the example.
// Inlined, stripped-down version of the relevant part of typescript-string-enums.
function Enum<V extends string>(...values: V[]): { [K in V]: K };
function Enum<
T extends { [_: string]: V },
V extends string
>(definition: T): T;
function Enum(...values: any[]): object {
// ... elided for brevity ...
return {} as any;
}
type Enum<T extends object> = T[keyof T];
// Version 1: Using a "direct" string literal type.
// type MyEnum = "ENUM";
// Version 2: Using an "indirect" string literal type.
const MyEnum = Enum("ENUM",);
type MyEnum = Enum<typeof MyEnum>;
// The offending code sample.
interface MyInterfaceWithEnum {
foo: MyEnum;
}
function parseToEnum(s: string): MyEnum {
return null as any;
}
function failingFunctionWithMap(s: string): MyInterfaceWithEnum[] {
// This block fails to compile when using an "indirect" string literal type, claiming that
// Type 'string' is not assignable to type '"ENUM"'.
// This implicit change in type does not happen with the "direct" string literal type.
return [""].map((i) => ({
// Note that VSCode correctly reports the output of `parseToEnum` here for both enums.
foo: parseToEnum(i),
}));
}
function successfulFunctionWithoutMap(s: string): MyInterfaceWithEnum {
// This block always compiles, using either of the "direct" or "indirect" string literal types.
return {
foo: parseToEnum(s),
};
}
Expected behavior:
Both definitions of MyEnum
are equal and interchangeable.
Actual behavior:
The "indirect" type erroneously fails to compile in one of the two functions.