获取只读数组元素成员的 TypeScript 类型



我有一个名为PokemonDatareadonly 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;

游乐场链接

最新更新