typescript - 具有特定键和值集的 TypeScript 类型
问题描述
更新后的版本
完整的游乐场在这里
https://www.typescriptlang.org/play?#code/C4TwDgpgB...
我正在尝试创建一个只允许特定键的对象类型,但是对于每个键,您必须将特定的泛型类型扩展为值。IE
type AllowedKeys = "a" | "b" | "c";
type MyGenericType<T extends AllowedKeys> = {
type: T;
}
type MyObjectTypeGuard = {
[T in AllowedKeys]: MyGenericType<T>
}
// This is what I am aiming at, I tried to make sure that I now have an interface the should prevent me from using the only allowed keys with the allowed values
interface MyObject extends MyObjectTypeGuard {
a: { type: "a" } & { foo: "foo" };
b: { type: "b" } & { bar: "bar" };
c: { type: "c" } & { baz: "baz" };
}
现在这可以防止我使用具有错误值的键
// I cannot accidentially use the wrong base types
interface MyObject2 extends MyObjectTypeGuard {
a: { type: "b" } & { foo: "foo" };
b: { type: "b" } & { bar: "bar" };
c: { type: "c" } & { baz: "baz" };
}
// Or use completely wrong types even
interface MyObject3 extends MyObjectTypeGuard {
a: { foo: "foo" };
b: { type: "b" } & { bar: "bar" };
c: { type: "c" } & { baz: "baz" };
}
但我仍然可以做其他我希望自己阻止的事情
// But I can still miss a key
interface MyObject4 extends MyObjectTypeGuard {
a: { type: "a" } & { foo: "foo" };
b: { type: "b" } & { bar: "bar" };
}
// And I can still add arbitrary stuff
interface MyObject5 extends MyObjectTypeGuard {
a: { type: "a" } & { foo: "foo" };
b: { type: "b" } & { bar: "bar" };
c: { type: "c" } & { baz: "baz" };
fooBar: "baz";
}
我正在寻找一种方法来定义一个对象类型/接口,它强制我使用所有键(并且只有那些键)并让我为每个值扩展一个特定的泛型类型
旧版
我们有一个
BodyNode
可以扩展为变体的基本模型:type BodyNode<T extends NodeType> = { readonly type: T; }; type NodeWithText<T extends NodeType> = BodyNode<T> & WithText; type NodeWithChildren<T extends NodeType> = BodyNode<T> & WithChildren; type NodeWithReference<T extends NodeType> = BodyNode<T> & WithReference; interface WithText { readonly text: string; } interface WithChildren { readonly children: Node[]; } interface WithReference { readonly href: string; } ``` to be able to iterate over the node types, we created an interface that maps `NodeType`s to variants types: ```typescript type BodyNodes = { [T in NodeType]: BodyNode<T>; }; interface Nodes extends BodyNodes { headline: NodeWithText; paragraph: NodeWithChildren; anchor: NodeWithReference; } type Node = Nodes[keyof Nodes]; ``` implementation: ```tsx type Components = { [K in BodyNode]: React.FC<{ node: Nodes[K] }>; }; const components: Components = { headline: Headline, paragraph: Paragraph, anchor: Anchor, }; const Body = (props: { nodes: Node[] }) => ( <div> {props.nodes.map(node => { const Component = components[node.type]; return <Component node={node} />; })} </div> ); ``` we are using the node types as keys for the `Nodes` as well as the `Components` and with this we can map the Nodes with the right types to the right components in type safe way. But this implementation has a big flaw: ```typescript type BodyNodes = { [T in NodeType]: BodyNode<T>; }; interface Nodes extends BodyNodes { headline: BodyNode<'headline'>; paragraph: BodyNode<'headline'>; // ^^^^^^^^ this will cause an error, which is what we want anchor: BodyNode<'anchor'>; yadda: 'foobar'; //^^^^^^^^^^^^^^^^ this is still possible because we can extend BodyNodes in // any way and that's not cool } ``` We'd love to find a way to write a type that allows only - specific keys as above - for specific keys only specific values as above - requires each key to be there - BUT forbids extending, ie. have an exact implementaition of that type and nothing else
解决方案
我不认为你可以用接口做到这一点:“扩展”总是意味着你可以扩展。但是下面是什么?这有点笨拙,因为我们在 <> 中定义了我们的新类型,但我认为它可以满足您的需求。
type AllowedKeys = "a" | "b" | "c";
type MyGenericType<T extends AllowedKeys> = {
type: T;
}
type MyObjectTypeGuard = { [T in AllowedKeys]: MyGenericType<T> };
type VerifyType<T extends MyObjectTypeGuard &
{ [U in Exclude<keyof T, AllowedKeys>]: never }> = T;
// Incorrect type letters or missing 'type', additional properties,
// and missing properties lead to errors
type VerifiedType = VerifyType<{
a: { type: "a" } & { foo: "foo" };
b: { type: "b" } & { bar: "bar" };
c: { type: "c" } & { baz: "baz" };
}>;
const obj: VerifiedType = {
a: { type: "a", foo: "foo" },
b: { type: "b", bar: "bar" },
c: { type: "c", baz: "baz" }
}
推荐阅读
- django - 过滤 DRF 中字段的多个值
- python - 如何从我自己的 fork 中导入 sympy(或带有 __init__.py 的另一个模块)?
- javascript - 是否可以在不更改样式的情况下将 html 转换为纯文本?
- python - 如何访问 xml 标签中以逗号分隔的元素?
- javascript - 电子.js | ipcRenderer 和 ipcMain 导致白屏/黑屏
- python - 为什么 tape.gradient 在我的 Sequential 模型中不返回所有内容?
- javascript - 保持复选框选中角度
- sql - 用另一列 postgresql 中的值替换字符串的一部分
- bash - 如何使用 sort 或 awk 对具有相同数量字段但一个字段的值不同的行进行排序?
- docker - 在 Docker 容器中使用 JMeter Prometheus 插件(连接被拒绝)