TypeScript接口内函数ReturnType问题:类型不匹配报错求解
问题描述
我始终搞不懂怎么编写这样一个函数:它接收某个特定接口的键,并返回对应正确类型的值。示例代码如下:
interface Transformers { boolean: (value: string) => boolean; string: (value: string) => string; } const transformers: Transformers = { boolean: (value) => Boolean(value), string: (value) => value, } function transformValue<T extends keyof Transformers>(value: string, type: T): ReturnType<Transformers[T]> { return transformers[type](value); }
我期望调用transformValue('lorem', 'boolean')时返回boolean类型,调用transformValue('lorem', 'string')时返回string类型,但却出现错误提示:Type 'string | boolean' is not assignable to type 'ReturnType<Transformers[T]>',找不到问题根源。
问题原因
TypeScript无法自动推断transformers[type](value)的具体类型与ReturnType<Transformers[T]>完全匹配。因为transformers被显式标注为Transformers类型后,transformers[type]会被识别为联合类型((value: string) => boolean) | ((value: string) => string),调用该联合函数返回的结果是string | boolean。而泛型T对应的返回类型是具体的单一类型,因此出现类型不兼容的错误。
解决方案
方案一:添加类型断言
直接通过类型断言告诉TypeScript返回值的类型符合预期:
function transformValue<T extends keyof Transformers>(value: string, type: T): ReturnType<Transformers[T]> { return transformers[type](value) as ReturnType<Transformers[T]>; }
方案二:保留精确类型推断(推荐)
不给transformers显式指定Transformers类型,改用as const satisfies确保其符合接口定义的同时,保留TypeScript推断的精确类型:
interface Transformers { boolean: (value: string) => boolean; string: (value: string) => string; } const transformers = { boolean: (value: string) => Boolean(value), string: (value: string) => value, } as const satisfies Transformers; function transformValue<T extends keyof typeof transformers>(value: string, type: T): ReturnType<typeof transformers[T]> { return transformers[type](value); }
这种方式无需额外断言,TypeScript能根据传入的type参数自动推断出正确的返回类型,同时satisfies Transformers保证了transformers的结构符合接口要求,避免类型错误。
方案三:通过条件分支缩小类型范围
如果接口成员较少,可以通过条件分支让TypeScript精确推断返回类型:
function transformValue<T extends keyof Transformers>(value: string, type: T): ReturnType<Transformers[T]> { if (type === 'boolean') { return transformers.boolean(value) as ReturnType<Transformers[T]>; } return transformers.string(value) as ReturnType<Transformers[T]>; }
不过这种方式在接口成员增多时会变得繁琐,仅适合简单场景。
内容的提问来源于stack exchange,提问作者rwacarter

