TypeScript中...args: never[]与...args: unknown[]的区别解析
为什么
...args: never[]能匹配任意toString方法?以及unknown和never的区别 一、...args: never[]可行的原因
TypeScript中函数参数遵循逆变兼容性规则,同时never是所有类型的子类型——它代表“不存在任何有效值的类型”。
当你把toString定义为(...args: never[]) => string时,这个签名本质上表示:该函数无法被调用(因为没有任何合法值能构成never[]类型的参数)。
从赋值兼容性的角度看:任何实际的toString方法(比如数字的(radix?: number) => string、自定义对象的(text: string) => string)都能赋值给这个签名。因为TS知道你永远无法通过Stringable类型调用toString(没有合法参数可传),所以不会出现调用时参数不匹配的安全问题,因此允许这种赋值。
二、unknown和never的核心区别
1. 类型层级与含义
unknown是所有类型的父类型:它代表“可能是任何类型,但具体类型未知”,你可以传入任意值作为unknown[]的参数。never是所有类型的子类型:它代表“不存在任何值的类型”,没有任何值能被赋值给never类型。
2. 场景下的兼容性差异
- 使用
(...args: unknown[]) => string时,签名要求toString能接受任意数量、任意类型的参数。但实际的toString方法(比如数字的toString仅接受可选的数字基数参数)无法满足这个要求——如果通过Stringable调用toString并传入字符串参数,实际的数字toString不支持,这会导致类型不安全,因此TS直接报错。 - 而
...args: never[]的签名因为无法被调用,完全规避了参数匹配的安全检查,所以所有带toString方法的对象都能兼容。
补充:更实用的替代方案
如果你的需求是“只要对象有toString方法即可”,且不需要通过Stringable类型调用toString(直接用原始类型调用),never[]的写法是可行的。但如果需要通过Stringable调用toString,可以根据实际需求选择:
- 如果只需要无参调用:用
{ toString(): string }(但自定义带参数的toString无法赋值)。 - 如果需要兼容任意参数调用:用
(...args: any[]) => string(注意这会失去类型检查,需自行保证调用安全)。
内容的提问来源于stack exchange,提问作者Remellion
相关产品推荐
相关产品推荐

