使用名称和类型属性的类型化值创建对象

时间:2020-06-19 08:27:25

标签: typescript typescript-generics

给出以下类型:

export interface PatchOperation<T> {
  from?: keyof T;
  op: OperationType;
  path: keyof T;
  value?: T[keyof T];
}

interface Parent {
  creation: Date;
  id: number;
  name: string;
}

如何强制PatchOperation<Parent>.value具有PatchOperation<Parent>.path的正确类型。

因此以下代码会产生错误:

const update: PatchOperation<Parent> = {
  op: OperationType.Replace,
  path: 'creation',
  value: 'string', // Type 'string' is not assignable to type 'Date'.
};

2 个答案:

答案 0 :(得分:1)

您可以使用distributive conditional type来改进您的解决方案,因此您无需显式传递键类型:

export type PatchOperation<T, K = keyof T> = K extends keyof T ? {
  op: OperationType;
  path: K;
  value?: T[K];
} : never;

现在我们得到了预期的错误:

// Type 'string' is not assignable to type 'Date | undefined'
const update: PatchOperation<Parent> = {
  op: OperationType.Replace,
  path: 'creation',
  value: 'string',
};

分布条件类型在实例化过程中自动分布在联合类型上

因此在我们的示例中,PatchOperation<Parent>将等效于:

{
    op: OperationType;
    path: "creation";
    value?: Date;
} | {
    op: OperationType;
    path: "id";
    value?: number;
} | {
    op: OperationType;
    path: "name";
    value?: string;
}

Playground

答案 1 :(得分:0)

不如我所希望的那样实用,但是它通过添加与属性名称相对应的第二个通用参数来起作用。

export interface PatchOperation<T, P extends keyof T> {
  from?: keyof T;
  op: OperationType;
  path: keyof T;
  value?: T[P];
}

interface Parent {
  creation: Date;
  id: number;
  name: string;
}

const update: PatchOperation<Parent, 'creation'> = {
  op: OperationType.Replace,
  path: 'creation',
  value: 'string', // Type 'string' is not assignable to type 'Date'.ts(2322)
};

const update: PatchOperation<Parent, 'creation'> = {
  op: OperationType.Replace,
  path: 'creation',
  value: new Date(), // ok
};

实际用途:

type PatchDocument<T, P extends keyof T> = PatchOperation<T, P>[];

const update = createAction(
    '[Project] Update Project',
    props<{
      operation : PatchDocument<ProjectDTO, keyof ProjectDTO>
    }>()
  );
相关问题