具有特定键和值集的TypeScript类型



更新版本

https://www.typescriptlang.org/play?#code/C4TwDgpgB...

我试图创建一个只允许特定键的对象类型,但对于每个键,您必须将特定的泛型类型扩展为值。即

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

我认为你不能用接口做到这一点:"extends"总是意味着你可以扩展。但是下面的内容如何?这有点笨拙,因为我们在<gt;,但我认为这是你想要的。

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" }
}

最新更新