typescript 我能把字符串Map到类型的类型吗?

k10s72fa  于 2023-02-10  发布在  TypeScript
关注(0)|答案(2)|浏览(135)

我有一个字符串文本类型,例如

type ConfigurationTypes = 'test' | 'mock'

有些类型

type MockType = { id: string }
type TestType = { code: string }

我想创建一个类型,将字符串“Map”到这些类型,这样如果ConfigurationTypes发生变化,我的类型MappedConfigurationTypes也会相应地发生变化,这可能吗?

type MappedConfigurationTypes: {[key in ConfigurationTypes]: any} = {
  test: TestType
  mock: MockType
}
xtfmy6hx

xtfmy6hx1#

从某种意义上说,你需要的是一个类型级的satisfies运算符,如果你写e satisfies T,其中e是一个表达式,T是一个类型,编译器会确保e可以赋值给T,而不会扩展到T。所以e保持它的原始类型,但是如果与T不兼容,你会得到一个错误。你想做同样的事情,但是用另一个类型替换表达式。

// this is invalid TS, don't do this:
type MappedConfigurationTypes = {
  test: testType; 
  mock: MockType
} Satisfies {[K in ConfigurationTypes]: any}

但是没有这样的Satisfies类型运算符,太糟糕了。
幸运的是,我们基本上可以自己建造一个:我们可以写Satisfies<U, T>而不是T Satisfies U(我将"Satisfies U"作为注意的语法单元,所以我希望使用Satisfies<U, T>而不是Satisfies<T, U>,但是您可以根据需要定义它)。
定义如下:

type Satisfies<U, T extends U> = T;

您可以看到Satisfies<U, T>的计算结果总是T,但是由于T被限制为U,因此如果TU不兼容,编译器将发出警告。
我们试试看:

type ConfigurationTypes = 'test' | 'mock';
type MockType = { id: string }
type TestType = { code: string }        

type MappedConfigurationTypes = Satisfies<{ [K in ConfigurationTypes]: any }, {
    test: TestType
    mock: MockType
}>

看起来不错。如果将鼠标悬停在MappedConfigurationTypes上,您会看到它相当于

/* type MappedConfigurationTypes = {
    test: TestType;
    mock: MockType;
} */

另一方面,如果向ConfigurationTypes联合体添加另一个成员,则会看到预期的错误:

type ConfigurationTypes = 'test' | 'mock' | 'oops'

type MappedConfigurationTypes = Satisfies<{ [K in ConfigurationTypes]: any }, {
    test: TestType
    mock: MockType,
}> // error!
//   Property 'oops' is missing in type '{ test: TestType; mock: MockType; }' but required 
//   in type '{ test: any; mock: any; oops: any; }'.

Playground代码链接

tyu7yeag

tyu7yeag2#

我对jcalz提供的代码做了一些调整,然后找到了一个稍微好一点的解决方案:

type MapFromLiteral<U extends string, T extends { [key in U]: any }> = T;

它可以用作

type MappedConfigurationTypes = MapFromLiteral<ConfigurationTypes, {
    test: TestType,
    mock: MockType
}>

相关问题