我正在使用一个枚举来实现名义上的打字(例如TypeScript Deep Dive书中所建议的):
enum ExampleIdBrand {}
export type ExampleId = ExampleIdBrand & string
const exampleId: ExampleId = '42' as ExampleId
const m1 = (e: ExampleId) => e.toUpperCase()
m1(exampleId) // ✅
到目前为止,一切正常。但是,如果我更改方法以接受(更广泛的)联合类型,则编译器将不再接受我的exampleId
:
const m2 = (e: ExampleId | 'whatever') => e.toUpperCase()
m2('whatever') // ✅
m2(exampleId) // Does not compile
为什么最后一行编译不正确? (TS 3.3.4000)
答案 0 :(得分:1)
在TypeScript中,枚举可以是数字或字符串。数字没有.toUpperCase()方法。
您的示例应该可以正常工作,因为枚举被缩小为字符串类型。
一种解决方法是:
enum ExampleIdBrand {}
export type ExampleId = ExampleIdBrand;
const exampleId: ExampleId = '42';
const m1 = (e: ExampleId) => e.toString().toUpperCase();
m1(exampleId);
const m2 = (e: ExampleId | 'whatever') => e.toString().toUpperCase();
m2('whatever'); // ✅
m2(exampleId); // ✅
答案 1 :(得分:1)
交集为空集(即没有值可以是交集的实例)发生的事情已经改变。尽管会一直寻找,但我找不到真正的文档,但是在某些情况下,此类交叉点会崩溃到永远。在这种情况下ExampleId | 'whatever' = never | 'whatever' = 'whatever'
const m2 = (e: ExampleId | 'whatever') => e.toUpperCase()
type x = Parameters<typeof m2>[0] // 'whatever'
要保持ExampleId
的名义性质,我们可以添加属性:
enum ExampleIdBrand {}
export type ExampleId = { __brand: ExampleIdBrand } & string
const exampleId: ExampleId = '42' as ExampleId
const m1 = (e: ExampleId | "whatever") => e.toUpperCase()
m1(exampleId) // ✅
m1("whatever")// ✅
或者,如果我们想很好地隐藏该成员,则可以使用带有私有字段的类的交集:
enum ExampleIdBrand { }
class Brand<T> { private __brand: T}
export type ExampleId = Brand<ExampleIdBrand> & string
const exampleId: ExampleId = '42' as ExampleId
const m1 = (e: ExampleId | "whatever") => e.toUpperCase()
m1(exampleId) // ✅
m1("whatever")// ✅
或删除枚举并使用此class ExampleIdBrand { private __brand!: any}