如何将联合类型中的类型用作类型别名?以及Type 'RowProp<RowType>'无法赋值给类型'RowProp<number>'错误的修复方案
问题1:如何将联合类型中的某一类型用作类型别名?
要从联合类型里单独提取某一个成员作为类型别名,TypeScript 提供了现成的工具类型Extract,这是最直接的方案。它的作用是从第一个类型参数中筛选出所有可以赋值给第二个类型参数的成员。
举个简单例子:
// 定义一个联合类型 type RowType = number | string; // 提取其中的number类型作为别名 type NumberRow = Extract<RowType, number>; // NumberRow 就是 number 类型
如果是更复杂的对象联合类型,也可以用同样的方式,通过结构特征来提取:
type Entity = { type: 'user', name: string } | { type: 'admin', role: string }; // 提取type为admin的类型 type AdminEntity = Extract<Entity, { type: 'admin' }>; // AdminEntity 等同于 { type: 'admin', role: string }
本质上,Extract的实现就是一个条件类型:
type Extract<T, U> = T extends U ? T : never;
你也可以自己写类似的自定义条件类型来实现更复杂的提取逻辑。
问题2:修复Type 'RowProp<RowType>' is not assignable to type 'RowProp<number>'错误
咱们先分析错误原因:你的TableData接口里,template要求的是能接受RowProp<number | string>的函数,但templateA只能接受RowProp<number>。这里涉及到函数参数的逆变规则——当你把一个接受更具体类型的函数赋值给接受更宽泛类型的函数类型时,TypeScript会报错,因为前者无法处理后者可能传入的所有参数(比如RowProp<string>)。
给你几个靠谱的修复方案:
方案1:让TableData变成泛型接口(推荐)
这是最安全、最符合TypeScript类型系统设计的方案,通过泛型让TableData的template参数类型和具体使用场景绑定:
export type RowType = number | string; export interface RowProp<T> { id: string; row: T; } // 把TableData改成泛型,默认值保留原有的RowType export interface TableData<T extends RowType = RowType> { id: string; template?: (e: RowProp<T>) => JSX.Element; } const templateA = (e: RowProp<number>) => <div></div>; // 使用时指定泛型参数为number,让template类型匹配templateA const data: TableData<number> = { id: '1', template: templateA };
方案2:放宽templateA的参数类型并添加类型守卫
如果不想用泛型,可以把templateA的参数类型改成RowProp<RowType>,然后在函数内部通过类型守卫确保只处理number类型的row:
const templateA = (e: RowProp<RowType>) => { // 类型守卫:确保e.row是number类型 if (typeof e.row === 'number') { // 这里可以安全地处理number类型的row return <div>{e.row}</div>; } // 处理string类型的情况,或者返回默认内容 return <div>非数字行</div>; }; const data: TableData = { id: '1', template: templateA };
这样templateA就能兼容TableData要求的函数类型,同时保证内部逻辑的类型安全。
方案3:使用类型断言(不推荐,仅确定场景下使用)
如果你能100%确定template永远只会传入RowProp<number>类型的参数,可以用类型断言跳过类型检查,但这会失去TypeScript的类型保护,不建议在不确定的场景下使用:
const data: TableData = { id: '1', template: templateA as (e: RowProp<RowType>) => JSX.Element };
内容的提问来源于stack exchange,提问作者Julian W.

