繁体   English   中英

为对象数组实施接口,并从映射值创建类型

[英]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"

看起来不错!

Playground 代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM