这里是 TypeScript 新手。我试图为“具有 .toString()
方法的事物”定义一个接口(interface),其中 .toString()
可以接受任意数量的参数,任何类型。
以下代码是一个最小示例,并且在界面中使用 ...args: never[]
作为 toString()
的参数似乎可以正常工作。我的问题是为什么;有人可以向我解释一下在这种情况下未知
和从不
之间有什么区别吗?
interface Stringable {
// toString: (...args: any[]) => string; // works but type-unsafe
// toString: (...args: unknown[]) => string; // causes num and object to error
toString: (...args: never[]) => string; // seems to work
}
const num: Stringable = 4;
const word: Stringable = "hello";
const obj: Stringable = { toString: (text: string) => text + "!!" };
console.log(num.toString());
console.log(word.toString());
console.log(obj.toString("world"));
最佳答案
never
是您永远无法分配任何内容但始终可以分配的类型
let x: never = 1; // can't assign annything to
let o: string = x; // can assign from
unknown
则相反,它是您始终可以分配给的类型,但您永远无法分配自
let x: unknown = 1; // can assign anything to
let o: string = x; // can't assign from
在函数签名的上下文中 (...args: never[]) => string
将是一个永远不能用任何参数调用的签名(因为我们永远不能将任何东西分配给never
类型的参数)。这样做的结果是,我们可以将任何函数实现分配给这样的签名,因为如果我们传入 never 类型的参数,因为 never
是可分配类型,因此 never
value 可分配给该参数的值。
interface Stringable {
toString: (...args: never[]) => string;
}
// Since this is not a error
const obj: Stringable = { toString: (text: string) => text.toUpperCase() + "!!" };
// This has to be a TS error
console.log(obj.toString(1));
一个不幸的后果是,虽然我们无法传入参数,但我们仍然可以调用不带参数的函数。这看起来像是类型系统中的一个不健全的地方。
interface Stringable {
toString: (...args: never[]) => string;
}
const obj: Stringable = { toString: (text: string) => text.toLowerCase() + "!!" };
console.log(obj.toString("X")); // ts error
console.log(obj.toString()); // callable, will throw a runtime error
(...args:unknown[]) => string
是一个函数签名,可以采用任何数字参数和任何值。这意味着实现必须能够处理传入的任何值。这意味着我们可以分配一个根本不处理其参数的函数(例如 String.toString(): string
),但是我们无法为其任何参数分配需要特定类型的参数(例如 Number.toString(radix?: number | undefined): string
或 obj.toString: (text: string ) => 字符串
)
直观上我们可以看到我们想要一个错误:
interface Stringable {
toString: (...args: unknown[]) => string;
}
// IF this is not an error
const obj: Stringable = { toString: (text: string) => text.toUpperCase() + "!!" };
// this is a runtime error
console.log(obj.toString(1));
原则上我们不能创建一个可以安全保存以下所有签名的函数签名:Number.toString(radix?: number | undefined): string
、obj.toString: (文本:字符串)=> 字符串
,String.toString():字符串
。我们不能这样做,因为 Number.toString
和 obj.toString
期望将冲突的类型作为第一个参数。调用两者的唯一安全方法是使用满足两个签名的参数,即 string
和 number
(即 string & number
)是从不
。
我们唯一可以安全地表示没有参数的 toString
:
interface Stringable {
toString: () => string;
}
const num: Stringable = 4;
const word: Stringable = "hello";
const obj: Stringable = { toString: (text?: string) => text + "!!" };
console.log(num.toString());
console.log(word.toString());
console.log(obj.toString());
关于typescript - TypeScript 中的 "...args: never[]"与 "...args: unknown[]",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75183307/