这里有 TS 的菜鸟。我正在 React 中创建一个自定义图标组件,我只想使用圆形材质 UI 图标。
我像这样定义类型:
import * as icons from '@material-ui/icons/index'
export type MaterialUiIcon = keyof typeof icons
这有效,但只能让我检查所有材料 UI 图标的类型。
相反,这就是我想要做的:
const roundedIconsNames = (Object.keys(icons).filter((icon) => icon.includes('Rounded')))
export type RoundedMaterialUiIcon = typeof roundedIconsNames[number]
然而 RoundedMaterialUiIcon 最终成为 string[] 类型
我怎样才能做到这一点?
谢谢。
答案 0 :(得分:1)
您收到 string[]
是因为 Object.keys
总是按设计返回 string[]
。
我认为最好制作 typeguard 和实用函数:
import * as icons from '@material-ui/icons/index'
type Icons = typeof icons
export type MaterialUiIcon = keyof Icons
type Prefix<T extends string> = `${string}${T}` | `${string}${T}${string}` | `${T}${string}`
// self explanatory, we have 3 variant of word
type Test1 = Prefix<'Rounded'> // `${string}Rounded` | `${string}Rounded${string}` | `Rounded${string}`
type GetByPrefix<T, P> = T extends P ? T : never;
/**
* This will return only HelloRounded, because union 'HelloRounded' | 'Batman'
* extends `${string}Rounded` | `${string}Rounded${string}` | `Rounded${string}`
*
* WHy extends?
* Because Rounded is at the end
*/
type Test2 = GetByPrefix<'HelloRounded' | 'Batman', Prefix<'Rounded'>> // 'HelloRounded'
/**
* This will return "RoundedWorld" because Rounded is at the beginning
*/
type Test3 = GetByPrefix<'RoundedWorld' | 'Batman', Prefix<'Rounded'>> // "RoundedWorld"
/**
* This is a special syntax for user defined typeguards
* It may help you to narrow the type
* https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
*/
const typeguard = <T extends string,>(tag: string) => (icon: MaterialUiIcon): icon is GetByPrefix<MaterialUiIcon, Prefix<T>> => icon.includes(tag)
const getRounded = <T extends string>(icons: Icons, includes: T) =>
/**
* Object.keys will always return string[], this is by design
* So you need to use type assertion here
*
* Array.prototype.filter accept curried typeguard
* It is a good practive yo use user defined typeguards a a predicate
* callback in array methods
*/
(Object.keys(icons) as Array<MaterialUiIcon>).filter(typeguard<T>(includes))
const result = getRounded(icons, 'Rounded')
请记住,由于 Icons
有 5K 联合类型,因此 TS 遍历所有联合并非易事。