[英]Enforce interface for array of objects and also create type from a mapped value
我正在使用 typescript 来确保队列满足IQueue
接口:
export interface IQueue {
id: string;
handler: () => void;
}
const queues:IQueue[] = [
{ id: 'a', handler: () => { } },
{ id: 'b' }, // handler is missing, should be an error
];
我还想要一个QueueId
类型,它是所有 id 的联合:
const queues = [
{ id: 'a', handler: () => { } },
{ id: 'b' },
] as const;
export declare type QueueId = (typeof queues[number])['id'];
export const start = (queueId:QueueId) => {
...
};
start('z'); // should be a typescript error
但我不能让他们一起工作。 QueueId
类型需要as const
类型。 有几篇文章建议进行 noop 强制转换,但我得到readonly cannot be assigned to the mutable type...
错误。 所以我试着让它可写,但它给出了一个“重叠不足”的错误:
type DeepWriteable<T> = { -readonly [P in keyof T]: DeepWriteable<T[P]> };
(queues as DeepWriteable<typeof queues>) as IQueue[];
有可能两者都做吗?
这是一个完整的例子:
首先,如果您希望编译器推断id
属性的字符串文字类型而不推断queues
的readonly
元组类型,那么您可以将const
断言从queues
初始化程序移动到相关的id
属性:
const queues = [
{
id: 'x' as const,
handler: () => { },
},
{
id: 'y' as const,
handler: () => { },
},
];
/* const queues: ({
id: "x";
handler: () => void;
} | {
id: "y";
handler: () => void;
})[] */
type QueueId = (typeof queues[number])['id'];
// type QueueId = "x" | "y"
此时,您要检查queues
的类型是否可分配给IQueue[]
而不实际将其注释为IQueue[]
,因为这会使编译器完全忘记"x"
和"y"
。
TypeScript 目前没有内置类型运算符来执行此操作; 在microsoft/TypeScript#47920有一个(暂时)称为satisfies
的功能请求,您可能会在其中编写类似
// this is not valid TS4.6-, don't try it:
const queues = ([
{
id: 'x' as const,
handler: () => { },
},
{
id: 'y' as const,
handler: () => { },
},
]) satisfies IQueue[];
然后,如果您遗漏了handler
或其他东西,编译器会抱怨。 但是没有satisfies
运营商。
幸运的是,您基本上可以编写一个助手 function (如果您眯着眼睛看它),它的行为就像一个satisfies
运算符。 不要写x satisfies T
,而是写satisfies<T>()(x)
。 这是你写它的方式:
const satisfies = <T,>() => <U extends T>(u: U) => u;
那里的额外()
是因为satisfies
是一个curried function以便允许您在编译器推断U
时手动指定T
。 有关详细信息,请参阅Typescript:在可选的第一个泛型之后推断泛型的类型。
反正我们在使用的时候,我们可以看到如果你搞砸了它会报错:
const badQueues = satisfies<IQueue[]>()([
{
id: 'x' as const,
handler: () => { },
},
{ id: 'y' as const }, // error!
// ~~~~~~~~~~~~~~~~~ <-- Property 'handler' is missing
]);
当你不搞砸时,它不会忘记'x'
和'y'
:
const queues = satisfies<IQueue[]>()([
{
id: 'x' as const,
handler: () => { },
},
{
id: 'y' as const,
handler: () => { },
},
]);
/* const queues: ({
id: "x";
handler: () => void;
} | {
id: "y";
handler: () => void;
})[]
*/
type QueueId = (typeof queues[number])['id'];
// type QueueId = "x" | "y"
看起来不错!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.