typescript - 如何使一个对象属性依赖于泛型类型中的另一个对象属性?

标签 typescript typescript-typings typescript-generics

我正在尝试创建一个具有依赖于另一个属性的属性的对象。

这是我迄今为止尝试过的一个非常简单的示例。
我期望从 name 推断出 T。然后,value 应限制为 TypeA 中的有效值。

type TypeA = {
  some: 'some2';
  thing: 'thing2';
};

type TypeAUnion = keyof TypeA;

type TestType<T extends TypeAUnion = TypeAUnion> = {
  name: T;
  value: TypeA[T];
};

const test1: TestType = {
  name: 'some',
  value: 'some2',
};

const test2: TestType = {
  name: 'some',
  value: 'thing2', // shouldn't be allowed here
};

编辑:

一个更好的例子来说明我正在尝试做的事情。

type StateType = {
  thingA: string;
  thingB: number;
};

type StateKeysUnion = keyof StateType;

const state: StateType = {
  thingA: 'somestring',
  thingB: 10,
};

type PayloadType<T extends StateKeysUnion = StateKeysUnion> = {
  key: T;
  value: StateType[T];
};

const setThing = (payload: PayloadType) => {
  state[payload.key] = payload.value;
};

setThing({
  key: 'thingA',
  // expected to only accept string
  value: true,
});


setThing({
  key: 'thingB',
  // expected to only accept number
  value: 'asdas',
});

最佳答案

看起来您想拿走原来的 StateType类型

type StateType = {
    thingA: string;
    thingB: number;
};

并制作PayloadType一个unionkey/value keyof StateType 中每个属性的对:

type PayloadType = {
    key: "thingA";
    value: string;
} | {
    key: "thingB";
    value: number;
}    

这只会让您分配正确的值,并且如果您不匹配 key 则会提示和value :

let payloadType: PayloadType;
payloadType = { key: "thingA", value: "abc" }; // okay
payloadType = { key: "thingA", value: 123 }; // error, number is not string
payloadType = { key: "thingB", value: 123 }; // okay

您可以定义PayloadType以编程方式将其编写为分布式对象类型,即 mapped type我们立即进入每个属性 index通过键的并集来得到我们关心的对象类型的并集:

type PayloadType = { [P in keyof StateType]: {
    key: P;
    value: StateType[P];
} }[keyof StateType];

您的进一步示例如下所示:

const state: StateType = {
    thingA: 'somestring',
    thingB: 10,
};

const setThing = (payload: PayloadType) => {
    state[payload.key] = payload.value; // error!
    //^^^^^^^^^^^^^^^^ <-- string | number not assignable to never
};

即使有PayloadType如果是正确的,编译器会提示 state[payload.key] = payload.value 。作业的右侧是 string | number ,但左侧必须是 string & number因为microsoft/TypeScript#30769 。 TypeScript 不直接支持相关联合,如 microsoft/TypeScript#30581 中所述。 。它看到payload.key属于 "thingA" | "thingB" 类型和payload.value作为 string | number 类型,并且它会担心您可能会分配 stringstate.thingA 。它看不出这是不可能的。

处理此问题的推荐方法是创建 setThing()以特定方式通用,如 microsoft/TypeScript#47109 中所述。 .

首先我们制作PayloadType再次通用,但仍然作为分布式对象类型。如果你只是写PayloadType单独或 PayloadType<keyof StateType> ,这是完整的联盟。但如果你写PayloadType<K1>对于某些特定属性(property)K1 (如 "thingA" ),您只得到并集的相应元素:

type PayloadType<K extends keyof StateType = keyof StateType> = { [P in K]: {
    key: P;
    value: StateType[P];
} }[K];

现在setThing()的实现工作:

const setThing = <K extends keyof StateType>(payload: PayloadType<K>) => {
    state[payload.key] = payload.value; // okay
};

这是因为赋值两边的类型都是 StateType[K] .


让我们验证一下 setThing()从调用者的角度也可以按需要工作:

setThing({ key: "thingA", value: "abc" }); // okay
// const setThing: <"thingA">(payload: { key: "thingA"; value: string; }) => void

setThing({ key: "thingA", value: 123 }); // error!
// ---------------------> ~~~~~

setThing({ key: "thingB", value: 123 }); // okay
// const setThing: <"thingB">(payload: { key: "thingB"; value: number; }) => void

看起来不错。编译器推断 K作为"thingA""thingB"取决于key属性,然后检查 value反对它。

Playground link to code

关于typescript - 如何使一个对象属性依赖于泛型类型中的另一个对象属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74618270/

相关文章:

typescript - 自动字符串枚举

node.js - TypeORM 获取多对多关系的一侧

Typescript 从实现中推断通用类型

函数类型和参数类型映射之间的 typescript 匹配

typescript - 应用函数名称和参数的函数类型

typescript - Sequelize V6 typescript : get data as plain object with findall

javascript - 警告 : React does not recognize the X prop on a DOM element

javascript - 如何使用Typescript定义强类型电子邮件模板参数?

typescript - 如何一般地键入返回对象数组的值数组的函数?

javascript - typescript 中具有某种类型的任何属性的通用类型