是否可以在下面的示例中隐式说明
key
属性的可能选项(基于 name
):
type TypeMap = {
A: 1 | 2 | 3;
B: 4 | 5 | 6;
};
type InnerType<TKey extends keyof TypeMap> = {
key: TKey;
name: TypeMap[TKey];
// + other properties;
};
type OuterType = {
x: InnerType; // Should be agnostic at this point
y: InnerType; // but typescript requires a generic definition
};
const foo: OuterType = {
x: { key: "A", name: 2 },
y: { key: "B", name: 4 },
};
我能想到的最接近的是如下所示,(但我宁愿不必更改相应类型的格式):
type TypeMap = {
A: 1 | 2 | 3;
B: 4 | 5 | 6;
};
type InnerType = {
data: Partial<{ [key in keyof TypeMap]: TypeMap[key] }>;
// + other properties
};
type OuterType = {
x: InnerType;
y: InnerType;
};
const foo: OuterType = {
x: { data: { A: 2 } }, // not blocking me from using `{}` or `{ A: 2, B: 4 }` on the same object
y: { data: { B: 4 } },
};
另一种方法如下(但不基于密钥进行限制):
type TypeMap = {
A: 1 | 2 | 3;
B: 4 | 5 | 6;
};
type InnerType = {
key: keyof TypeMap;
name: TypeMap[keyof TypeMap];
// + other properties;
};
type OuterType = {
x: InnerType;
y: InnerType;
};
const foo: OuterType = {
x: { key: "A", name: 2 }, // allows me to use `{ key: "A", name 4 }`
y: { key: "B", name: 4 },
};
您的
InnerType
版本是单个对象类型,其属性为 unions,它允许以您不希望的方式混合 key
和 name
。您确实希望 InnerType
本身成为对象类型的联合,如下所示:
type InnerType = {
key: "A";
name: 1 | 2 | 3;
} | {
key: "B";
name: 4 | 5 | 6;
}
这给了你你想要的行为:
const foo: OuterType = {
x: { key: "A", name: 1 },
y: { key: "B", name: 5 }
};
const badFoo: OuterType = {
x: { key: "A", name: 4 }, // error!
// Type '{ key: "A"; name: 4; }' is not assignable to type 'InnerType'.
// Type '4' is not assignable to type '1 | 2 | 3'.
y: { key: "A", name: 2 }
}
您甚至可以用
InnerType
来写 TypeMap
,这样当 TypeMap
更新时它会自动更新:
type InnerType = { [K in keyof TypeMap]:
{ key: K, name: TypeMap[K] }
}[keyof TypeMap]
这个
InnerType
是在microsoft/TypeScript#47109中创造的分布式对象类型。它的工作原理是映射K
的键
TypeMap
,计算每个键
K
所需的对象类型,然后使用
keyof TypeMap
索引到映射类型,从而得到所需的并集。
InnerType
generic 以便
InnerType<K>
对应于特定的
K
constrained 到
keyof TypeMap
,您可以这样做:
type InnerType<K extends keyof TypeMap = keyof TypeMap> =
{ [P in K]:
{ key: P, name: TypeMap[P] }
}[K]
type InnerTypeA = InnerType<"A">
/* type InnerTypeA = {
key: "A";
name: 1 | 2 | 3;
} */
type InnerTypeB = InnerType<"B">
/* type InnerTypeB = {
key: "B";
name: 4 | 5 | 6;
} */
并注意 K
defaults 为
keyof TypeMap
,因此没有泛型类型参数的
InnerType
相当于完整的联合:
type InnerTypeBoth = InnerType
/* type InnerTypeBoth = {
key: "A";
name: 1 | 2 | 3;
} | {
key: "B";
name: 4 | 5 | 6;
} */
因此,您可以根据您的需要获得类似泛型和类似联合的行为。