TLDR;从数组派生类型没有按预期工作(Stacklitz对此进行了说明)。
为了改进代码库的某些部分,我偶然发现了几个不同的数组,这些数组用于将后台的信息映射到人类可读的字符串中,如下所示:
export const MyMapping = [
{ dbLabel: 'something', screenLabel: 'Something User-friendly' },
...
];
由于这在几个地方都存在;合同;的数组没有被某种类型强制执行,我继续写道:
export type DbLabelMapper = Record<'dbLabel' | 'screenLabel', string>;
(实际上第一个版本使用了一个接口,但想法是一样的)
然后,由于数据库标签在代码库的其他部分被用作类型,但被错误地用作string
s,我继续做了以下操作:
export const MyMapping: Array<DbLabelMapper> = [
{ dbLabel: 'something', screenLabel: 'Something User-friendly' },
...
] as const;
export type MyMappingType = typeof MyMapping[number]['dbLabel'];
Typescript对我大喊大叫,因为我不应该将只读类型([...] as const
)分配给可变类型(Array<DbLabelMapper>
)。因此,我对映射签名进行了如下更正:
export const MyMapping: Readonly<Array<DbLabelMapper>> = [
{ dbLabel: 'something', screenLabel: 'Something User-friendly' },
...
] as const;
export type MyMappingType = typeof MyMapping[number]['dbLabel'];
现在,在那之后,我的MyMappingType
就没有任何类型了。根据我的例子,我想要的是MyMappingType = 'something' | 'anotherThing' | '...'
。我是做错了什么,还是错过了什么?
您为MyMapping
提供了Readonly<Array<DbLabelMapper>>
的显式类型。使用typeof MyMapping
将返回此确切类型。
要解决此问题,您必须删除类型注释,并让TypeScript推断类型。
export const MyMapping = [
{ dbLabel: 'something', screenLabel: 'Something User-friendly' },
] as const
但是您将失去以前的类型安全性,因为您现在可以为MyMapping
分配任何值。
TypeScript 4.9将引入satisfies
运算符来帮助解决这种情况。
export const MyMapping = [
{ dbLabel: 'something', screenLabel: 'Something User-friendly' },
] as const satisfies Readonly<Array<DbLabelMapper>>
export type MyMappingType = typeof MyMapping[number]['dbLabel'];
// ^? type MyMappingType = "something"
游乐场