我有一个名为PokemonData
的readonly Pokemon[]
数组。 每个元素都有一个name
成员。 是否可以制作一个type
,即口袋妖怪名称的串联列表?
interface Pokemon {
readonly name: string;
// ...
}
const PokemonData: readonly Pokemon[] = [{
name: 'Pikachu',
// ...
}, {
name: 'Bulbasaur',
// ...
}] as const;
type PokemonName = // TODO: should be 'Pikachu' | 'Bulbasaur'
如果我只有一个名称数组,我可以很容易地做到这一点:
const pokemonNames = [ 'Pikachu', 'Bulbasaur' ] as const;
type ArrayType<T> = T extends readonly (infer U)[] ? U : never;
type PokemonName = ArrayType<typeof pokemonNames>;
我怀疑,如果这有可能的话,我需要上面ArrayType<T>
的对象属性等效项和/或一个接受Pokemon
并返回其名称的函数。
// using a type approach (doesn't work)
type GetPokemonName<T extends Pokemon> = T['name'];
type GetPokemonNames<T> = T extends Pokemon[] ? GetPokemonName<T[/* something here */]> : never;
// using a function approach (may work, but I don't know how to convert it to a type)
function getPokemonName(pkmn: Pokemon) {
return pkmn.name;
}
我还认为也许ReadonlyArray<T>.map()
会很有用,但它只是返回一个字符串类型。
const pokemonNames = PokemonData.map(p => p.name); // using 'as const' throws a compile-time error
type PokemonName = ArrayType<typeof pokemonNames>;
Typescript 能够将数组中的名称视为字符串文字类型的唯一方法是删除: readonly Pokemon[]
类型注释;否则,任何typeof
都将查看Pokemon
类型的声明,而不是从数组的实际内容推断出更具体的类型。
如果删除该批注,则可以将名称映射到联合类型:
const PokemonData = [{
name: 'Pikachu'
}, {
name: 'Bulbasaur'
}] as const;
type PokemonName = (typeof PokemonData)[number]['name'];
[number]
将数组类型映射到其组件类型,然后['name']
映射到与name
属性关联的字符串文本类型的联合。
这不是一个理想的解决方案,因为删除Pokemon[]
类型注释意味着 Typescript 不会检查其内容是否Pokemon
对象有效。如果需要,可以编写一个辅助函数:
function pokemon<K extends string>(obj: Pokemon & { name: K }): Pokemon & { name: K } {
return obj;
}
const PokemonData = [
pokemon({
name: 'Pikachu',
}),
pokemon({
name: 'Bulbasaur',
}),
] as const;
游乐场链接