强制一个字段的类型仅包含其他字段数组中的键

时间:2019-11-30 13:04:59

标签: typescript generics conditional-types

如何强制对象的键仅在它们实际包含在联合类型的数组中时才有效?

type Category = 'software' | 'hardware' | 'books'

type Item = {
    categories: Category[];
    registry: Partial<{[key in Category]: string}>
}


//Should be invalid because 'books' is not in categories array:
const myInValidItem: Item = {
    categories: ['software', 'hardware'],
    registry: { books: 'blabla' }
}

Playground Link

2 个答案:

答案 0 :(得分:0)

最简单的解决方案可能是定义一个辅助函数,以检查相关的registrycategories类型:

function createParts<T extends Category[]>(
  categories: T, registry: { [K in T[number]]?: string }
) {
  return { categories, registry };
}

const myInValidItem: Item = createParts(
  ["software", "hardware"],
  { books: "blabla" }
) // error, books not contained

您还可以创建限制性更强的Item类型并将其用于函数参数:

const myInValidItem = {
    categories: ['software', 'hardware'],
    registry: { books: 'blabla' }
} as const // const assertion here to preserve the tuple type

type ConstrainedItem<T extends readonly Category[]> = {
    categories: T;
    registry: Partial<{ [key in T[number]]: string }>
}

declare function doSomething<T extends readonly Category[]>(item: ConstrainedItem<T>): void

doSomething(myInValidItem) // error
doSomething(myValidItem)

Playground 1Playground 2

答案 1 :(得分:0)

与ford04提出的解决方案相同,但解释略有不同:

type Category = 'software' | 'hardware' | 'books';

const createItem = <T extends Category>(item: {
  categories: T[];
  registry: { [key in T]?: string }
}) => item;

//Should be invalid because 'books' is not in categories array:
const myInValidItem = createItem({
  categories: ['software', 'hardware'],
  registry: { 'books': 'blabla' }
});

Playground link