为何TypeScript在检查string类型后,仍对string|number泛型的+运算报错?
TypeScript联合类型收窄疑问:已检查string类型后剩余分支为何报错?
我尝试实现一个支持两数相加或两字符串拼接的函数,代码如下:
type Add = <T extends string | number>(a: T, b: T) => T; const add: Add = (a, b) => { if (typeof a === 'string' && typeof b === 'string') { return a + b; } return a + b; // 为什么这里报错?我们已经检查了一种类型,剩下的应该只有另一种类型了 } const result = add('a', 'b');
tsconfig.json配置:
// tsconfig.json "compilerOptions": { "target": "ESNext", "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", "allowJs": true, "noEmit": true, "strict": true, "noImplicitAny": true, "types": ["vinxi/types/client", "bun-types"], "isolatedModules": true, "paths": { "~/*": ["./src/*"] } }
问题解析
核心原因是泛型T的约束是string | number,但TypeScript无法将泛型参数本身收窄:
- 泛型
T在函数调用时确定,它可能被显式指定为string | number联合类型(比如add<string | number>(1, "2")),此时剩余分支中a和b的类型仍是string | number,a + b的结果会是string,但函数返回值要求是T(即string | number),类型不匹配。 - 类型收窄只作用于具体值的类型,而非泛型参数的约束。你检查了
a和b是string,但TypeScript无法推断出“此时T必然是number”——因为T本身可能包含两种类型。
解决方案
方案1:类型断言明确剩余类型
通过断言明确剩余分支中a和b为number,再断言返回值为T:
const add: Add = (a, b) => { if (typeof a === 'string' && typeof b === 'string') { return a + b; } return (a as number) + (b as number) as T; }
方案2:使用函数重载
放弃泛型,用重载签名让TypeScript明确参数与返回值的对应关系:
function add(a: string, b: string): string; function add(a: number, b: number): number; function add(a: string | number, b: string | number) { if (typeof a === 'string' && typeof b === 'string') { return a + b; } return Number(a) + Number(b); }
方案3:条件类型约束泛型
通过条件类型让返回值类型与T严格匹配:
type Add = <T extends string | number>(a: T, b: T) => T extends string ? string : number; const add: Add = (a, b) => { if (typeof a === 'string' && typeof b === 'string') { return a + b; } return a + b as T; }
内容的提问来源于stack exchange,提问作者Md. A. A. Apu
相关产品推荐
相关产品推荐

