我正在尝试创建一个具有依赖于另一个属性的属性的对象。
这是我迄今为止尝试过的一个非常简单的示例。
我期望从 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
一个union的key
/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
类型,并且它会担心您可能会分配 string
至state.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
反对它。
关于typescript - 如何使一个对象属性依赖于泛型类型中的另一个对象属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74618270/