英文:
Referencing an object's keys in a property from another property
问题
我确定这比我想的简单,但我真的没有找到任何方法。用一个简单的场景来解释会更容易:
您可以看到在食谱属性中,值表示的是一些简单的字符串配料。我该如何指示这些字符串应该是配料对象的一个键呢?
// 这应该是无效的,因为“pineapple”不是配料属性中的键。
"pizza": ["tomato", "cheese", "pineapple"],
英文:
I'm sure this is simpler than I think but I really haven't found anything. It's easier to explain with a simple scenario:
const cookbook: CookBook = {
ingredients: {
"tomato": { vegetal: true },
"cheese": { vegetal: false },
"lettuce": { vegetal: true },
},
recipes: {
"pizza": ["tomato", "cheese"],
"salad": ["tomato", "lettuce"]
}
}
type CookBook = {
ingredients: {[key: string]: {vegetal: boolean}},
recipes: {[key: string]: string[]}
}
You can see that in the recipes property, the values represent lists of ingredients, which are mere strings. How could I indicate that those strings should be a key of the ingredients object?
...
recipes: {
// This should not be valid because "pineapple"
// is not a key in ingredients property.
"pizza": ["tomato", "cheese", "pineapple"],
...
}
}
答案1
得分: 2
您可以编写一个带有通用参数的函数。
我们正在根据推断的“Cookbook”对象构建一个有效的“ValidCookBook”对象并进行比较。这在IDE中提供了良好的开发人员体验。
“Narrow”用于防止TS扩展配方中的食材数组,尽管现在可能有更干净的方法来做这件事。
---
类型级别的替代方法如下:
```typescript
const cookbook = {
ingredients: {
"tomato": { vegetal: true },
"cheese": { vegetal: false },
"lettuce": { vegetal: true },
},
recipes: {
"pizza": ["tomato", "cheese"],
"salad": ["tomato", "lettuce"]
}
} as const;
{type Test = ValidateCookBook<typeof cookbook>;}
type ValidateCookBook <T extends CookBook & IsValidCookBook<T>> = never;
type IsValidCookBook<T extends CookBook> = {
ingredients: T['ingredients'],
recipes: { [K in keyof T['recipes']]: readonly (keyof T['ingredients'])[] }
}
type CookBook = {
ingredients: {[key: string]: {vegetal: boolean}},
recipes: {[key: string]: readonly string[]}
}
英文:
You can write a function with a generic parameter
const cookbook = createCookBook({
ingredients: {
"tomato": { vegetal: true },
"cheese": { vegetal: false },
"lettuce": { vegetal: true },
},
recipes: {
"pizza": ["tomato", "cheese"],
"salad": ["tomato", "lettuce"]
}
})
const createCookBook =
<T extends CookBook & IsValidCookBook<T>>
(cookbook: Narrow<T>) => cookbook
type IsValidCookBook<T extends CookBook> = {
ingredients: T['ingredients'],
recipes: { [K in keyof T['recipes']]: (keyof T['ingredients'])[] }
}
type Narrow<T> = {
[K in keyof T]
: K extends keyof [] ? T[K]
: T[K] extends (...args: any[]) => unknown ? T[K]
: Narrow<T[K]>
};
We are constructing a valid ValidCookBook
object based of the inferred Cookbook
object and comparing the two. This enables a good developer experience in the IDE.
Narrow
is used to prevent TS from widening the ingredients array in the recipes, although there are probably cleaner ways to do it now.
The type-level alternative looks like this
const cookbook = {
ingredients: {
"tomato": { vegetal: true },
"cheese": { vegetal: false },
"lettuce": { vegetal: true },
},
recipes: {
"pizza": ["tomato", "cheese"],
"salad": ["tomato", "lettuce"]
}
} as const;
{type Test = ValidateCookBook<typeof cookbook>}
type ValidateCookBook <T extends CookBook & IsValidCookBook<T>> = never;
type IsValidCookBook<T extends CookBook> = {
ingredients: T['ingredients'],
recipes: { [K in keyof T['recipes']]: readonly (keyof T['ingredients'])[] }
}
type CookBook = {
ingredients: {[key: string]: {vegetal: boolean}},
recipes: {[key: string]: readonly string[]}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论