我试图为一个主题创建一个严格的Map,但我希望Map的键不是动态的,而是有可选的键值。|AppBackground”可以有其中一个键和相同的值。下面是我尝试过的一些事情。我总是希望允许像red:color
这样的严格键值对。
type 300 = '300' | 'CardBackground';
export type ThemeMapping = {
[key: '100' | 'AppBackground']: Color;
[key in '200' | 'ContentBackground']: Color;
[key: 300 ]: Color;
['400' | 'InactiveBackground']: Color;
link: Color;
red: Color;
yellow: Color;
green: Color;
blue: Color;
}
我知道对于动态值,你也可以使用{[key: string]: Color}
。我只是希望键类型本质上是选项,而不是动态的,或者只是一个普通的字符串。
请让我知道如果你需要我解释更多。
2条答案
按热度按时间42fyovps1#
可以合并多个类型来实现此行为。
这就是你想要的吗
fhity93d2#
TypeScript本身并不支持“一个对象只具有某个集合中的一个属性”的概念,所以如果你想模拟它,你需要自己编写一些以这种方式工作的东西。这里有一种方法:
这个想法是
ExactlyOneProp<T>
应该导致一个联合类型,其中T
的每个属性都有一个成员,其中每个联合成员应该要求一个键并禁止其他成员。相反,您可以创建一个可选属性,其值是不可能的never
类型,因此唯一可接受的做法是忽略该属性(或者根据编译器选项将其设置为undefined
)。因此ExactlyOneProp<{a: 0, b: 1, c: 2}>
应该等效于{a: 0, b?: never, c?: never} | {a?: never, b: 1, c?: never} | {a?: never, b?: never, c: 2}
。ExactlyOneProp<T>
通过mapping在T
中的每个属性密钥K
上工作,并创建一个Pick<T, K> & {[P in Exclude<keyof T, K>]?: never}
类型的新属性(使用Pick<T, K>
实用程序类型作为仅具有已知K
属性的对象),然后将其与另一个Map类型(具有Exclude<keyof T, K>
中的键)交叉(使用Exclude<X, Y>
实用程序类型从T
的键中筛选出K
),所有可选属性(来自?
Map修饰符)的类型为never
。这会生成一个相当难看的类型,因此我们将通过
其实质上只是将所有对象交叉折叠成它们的等效单对象类型(例如,
{a: 0} & {b: 1}
变为{a: 0, b: 1}
)。让我们测试一下:
看起来不错
所以现在要按照你想要的方式生成
ThemeMapping
,我们需要将一堆不同的片段相交,像这样:这就变成了一个2 × 2 × 2 × 2 × 1 = 16个成员的联合,每个成员有2 + 2 + 2 + 2 + 5 = 13个属性,所以在这里一次显示它太多了。它看起来像:
您可以通过尝试不同的可能性来验证它是否按预期运行:
正如你所希望的,你必须定义
300
和CardBackground
属性中的一个,如果你试图定义两个或两个都不定义,你会得到一个错误。如果你愿意,你可以自己测试其他属性。Playground链接到代码