TypeScript如何约束泛型T为number|string并解决返回值类型报错
报错根因
- 泛型约束
T extends number | string仅限制T是联合类型的子类型,不代表T只能是number或string本身:它可以是任意字面量子类型,比如数字字面量1、字符串字面量"id"都符合约束。 - 当你直接返回
_.toNumber()产出的通用number类型、_.toString()产出的通用string类型时,TS无法确认该值匹配调用方指定的具体子类型T。例如调用方指定T为字面量2,函数返回普通number类型就不满足类型要求,因此抛出类型错误。 - 你尝试的
<T = number | string>写法只是给泛型设置了默认值,并没有改变泛型可被赋值为任意子类型的逻辑,因此无法解决报错。
推荐实现:函数重载
这是类型安全最高的写法,通过重载签名严格绑定type入参和返回值类型,不需要强制类型转换:
// 重载签名:传type为'number'时返回number类型 export function getItemFromLocalStorage(key: string, type: 'number'): number | undefined // 重载签名:传type为'string'时返回string类型 export function getItemFromLocalStorage(key: string, type: 'string'): string | undefined // 重载签名:不传type时默认返回string类型 export function getItemFromLocalStorage(key: string, type?: 'string'): string | undefined // 函数实现 export function getItemFromLocalStorage( key: string, type?: 'number' | 'string' ): number | string | undefined { const storedItem = localStorage.getItem(key) // 补全原代码缺失的null判断,localStorage取不到值时会返回null if (storedItem === null || _.isNaN(storedItem)) { return undefined } if (type === 'number') { return _.toNumber(storedItem) } return _.toString(storedItem) }
可选实现:泛型+类型断言
如果你必须保留泛型写法(比如后续要扩展支持更多类型),可以通过类型断言告知TS返回值符合T类型,注意这种写法需要你自行保证调用时传入的type参数和泛型T匹配,存在一定类型安全风险:
export const getItemFromLocalStorage = <T extends number | string = string>( key: string, type?: 'number' | 'string' ): T | undefined => { const storedItem = localStorage.getItem(key) if (storedItem === null || _.isNaN(storedItem)) { return undefined } if (type === 'number') { return _.toNumber(storedItem) as T } return _.toString(storedItem) as T }
风险提示:该写法下如果出现类似
getItemFromLocalStorage<number>('username', 'string')的调用,TS不会抛出错误,但实际运行会返回字符串类型,可能引发后续逻辑bug。
内容的提问来源于stack exchange,提问作者Amaranthus
相关产品推荐
相关产品推荐

