当我第一次看到“永不”这个词时,我以为TypeType在TypeScript中显得毫无用处。 随着时间的流逝,我深入了解ts,开始理解这个词的作用力。 而这种能力源于我打算与读者分享的实际用例。 谁在乎,欢迎来猫。
从来没有
如果深入研究历史,我们将看到TypeScript在2.0版TypeScript诞生之初就从未出现过,只是对其目的进行了
较为适度的描述 。 如果您简短自由地讲述ts开发人员的版本,则该类型从不为原始类型,该原始类型表示永远不会发生的值的符号。 或者,一个因其循环(例如,无限循环)或由于其中断而永不返回值的函数的符号。 为了清楚地说明所讲内容的实质,我建议看以下示例:
function error(message: string): never { throw new Error(message); } function infiniteLoop(): never { while (true) { } } function infiniteRec(): never { return infiniteRec(); }
也许由于这样的例子,我给人的第一印象是类型需要清晰。
类型系统
现在我可以说,TypeScript中的丰富类型系统动物群也永远不会到期。 为了支持我的话,我将提供lib.es5.d.ts中的几种库类型
type Exclude<T, U> = T extends U ? never : T; type Extract<T, U> = T extends U ? T : never; type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>; type NonNullable<T> = T extends null | undefined ? never : T; type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
在我从未使用过的类型中,我将给出我最喜欢的-GetNames,它是keyof的改进版:
type GetNames<FromType, KeepType = any, Include = true> = { [K in keyof FromType]: FromType[K] extends KeepType ? Include extends true ? K : never : Include extends true ? never : K }[keyof FromType];
监控未来的变化
当项目的寿命不短暂时,开发就具有特殊性。 我想控制代码中的关键点,以确保您或您的团队成员在花费时间时不会忘记修改重要的代码。 当存在某种状态或枚举时,此类代码部分尤其明显。 例如,列出实体上的一组动作。 我建议看一个例子以了解正在发生的情况。
上面的代码只是为了着重于重点而尽可能地简化了-AdminAction类型是在另一个项目中定义的,甚至可能没有团队陪伴。 由于该项目将持续很长时间,因此有必要在不知情的情况下保护ActionEngine免受AdminAction类型的更改。 TypeScript提供了几种解决此问题的方法,其中之一是使用Never类型。 为此,我们需要定义NeverError并在doAction方法中使用它。
class NeverError extends Error {
现在将新的“ BLOCK”值添加到AdminAction并在编译时出现错误:类型“ BLOCK”的参数不能分配给类型“ never” .ts的参数(2345)。
原则上,我们实现了这一目标。 值得一提的是有趣的一点是,开关构造可保护我们避免更改AdminAction元素或将其从集合中删除。 从实践中,我可以说它确实按预期工作。
如果您不想引入NeverError类,则可以通过声明一个Never类型的变量来控制代码。 像这样:
type AdminAction = "CREATE" | "ACTIVATE" | "BLOCK"; class ActionEngine { doAction(action: AdminAction) { switch (action) { case "CREATE":
上下文限制:此+永不
以下技巧常常使我免于因疲劳或粗心而造成的可笑错误。 在下面的示例中,我不会评估所选方法的质量。 事实上,与我们一起发生这种情况。 假设您在类中使用了无法访问该类字段的方法。 是的,这听起来很可怕-所有这些都是gov ...代码。
@SomeDecorator({...}) class SomeUiPanel { @Inject private someService: SomeService; public beforeAccessHook() {
在更广泛的情况下,它可以是回调或箭头函数,它们具有自己的执行上下文。 任务是:如何保护自己免受运行时错误的影响? 为此,TypeScript可以指定
此上下文。
@SomeDecorator({...}) class SomeUiPanel { @Inject private someService: SomeService; public beforeAccessHook(this: never) {
公平地说,我不会说这是不值得的。 相反,您可以使用void和{}。 但是,这种类型永远不会在您阅读代码时引起注意。
期望值
不变量
有一个绝对的想法,我认为以下代码应该可以工作:
type Maybe<T> = T | void; function invariant<Cond extends boolean>(condition: Cond, message: string): Cond extends true ? void : never { if (condition) { return; } throw new Error(message); } function f(x: Maybe<number>, c: number) { if (c > 0) { invariant(typeof x === "number", "When c is positive, x should be number"); (x + 1);
但是a。 表达式(x + 1)引发错误:运算符'+'不能应用于类型'Maybe'和'1'。 在文章
30,000行代码从Flow到TypeScript的传输中,我监视了示例本身
。灵活的绑定
我认为,在永不实现的帮助下,我可以控制必需的函数参数,并在某些情况下禁用不必要的参数。 但是不,那是行不通的:
function variants<Type extends number | string>(x: Type, c: Type extends number ? number : never): number { if (typeof x === "number") { return x + c; } return +x; } const three = variants(1, 2);
上述问题以
不同的方式解决。
更严格的验证
我希望ts编译器不要错过与常识相反的东西。
variants(<never> {}, <never> {});
结论
最后,我想提供一系列小怪异的小任务。 错误是哪一行?
class never<never> { never: never; } const whats = new never<string>(); whats.never = "";
选件在后者中:类型'“”'不能分配给类型'never'.ts(2322)
这就是我永远都不想说的。 谢谢大家的关注,我们很快再见。