TypeScript泛型报错咨询:‘DummyTableRow’无法赋值给‘Row’
这个报错的核心在于泛型参数之间缺乏明确的关联约束,再加上TypeScript 2.6.2版本对泛型默认值与推断的处理限制,才导致了这个类型不匹配的问题。
我们拆解一下你的泛型定义:
function add< Row extends object, T extends keyof RowChangeBag, L extends RowChangeList<Row> = RowChangeBag[T] >(...)
这里的Row是一个独立的泛型参数,仅被约束为object;而L的默认值RowChangeBag[T],当T为Dummy时对应的是RowChangeList<DummyTableRow>。但TypeScript无法自动推断出Row必须等于DummyTableRow——因为默认值只是“用户不指定时才用这个”,它不会反过来约束Row的类型。
换个角度说,TypeScript会认为你完全可以手动调用add时,指定Row为任意object子类型(比如{id: number}),同时指定T为Dummy。这时候RowChangeBag[T]是RowChangeList<DummyTableRow>,但L被约束为RowChangeList<Row>(也就是RowChangeList<{id: number}>),两者显然不兼容,所以它会提前抛出类型不匹配的错误。
为什么DummyTableRow无法赋值给Row?
因为Row是一个开放的泛型参数,它可以是任何object的子类型,而DummyTableRow是一个具体的、有固定结构的类型。TypeScript无法保证DummyTableRow一定是Row的子类型(毕竟你可能给Row指定一个更窄的类型,比如{id: number; name: 'fixed'}),所以会触发这个赋值错误。
这是不是TypeScript的Bug?
不是,这是早期TypeScript版本(2.6.2)中泛型系统的设计局限性。在这个版本里,泛型参数的默认值不会参与到其他泛型参数的推断或约束中,泛型参数之间的关联需要你手动显式定义。后续的TypeScript版本(比如3.4及以上)对泛型推断做了很多优化,但在2.6.2中这种写法确实会触发错误。
要解决这个问题,我们需要让Row的类型从T对应的RowChangeBag值中自动推断出来,而不是把它作为独立的泛型参数。可以通过条件类型提取RowChangeBag[T]中的行类型:
interface DummyTableRow { id: number; name: string; } interface RowChange<Row extends object> { newRow: Row | null; oldRow: Row | null; } interface RowChangeBag { Dummy: RowChangeList<DummyTableRow>; } type RowChangeList<Row extends object> = Array<RowChange<Row>>; function add< T extends keyof RowChangeBag, // 从RowChangeBag[T]中提取对应的Row类型 Row = RowChangeBag[T] extends RowChangeList<infer R> ? R : never >( bag: RowChangeBag, tableName: T, newRow: Row | null, oldRow: Row | null, ) { bag[tableName].push({ newRow, oldRow }); }
这样修改后,Row的类型会自动根据T的取值推断出来(比如当T为Dummy时,Row就是DummyTableRow),泛型参数之间建立了明确的关联,类型错误也就消失了。
内容的提问来源于stack exchange,提问作者Elmer

