概述

TypeScript 2.4 为标识符实现了拼写纠正机制。即使咱们稍微拼错了一个变量、属性或函数名,TypeScript 在很多情况下都可以提示正确的拼写。

TypeScript 2.7 支持 ECMAScript 的数字分隔符提案。 这个特性允许用户在数字之间使用下划线(_)来对数字分组(就像使用逗号和点来对数字分组那样)。

const worldPopulationIn2017 = 7_600_000_000;
const leastSignificantByteMask = 0b1111_1111;
const papayawhipColorHexCode = 0xFF_EF_D5;

数字分隔符不会改变数字字面量的值,但分组使人们更容易一眼就能读懂数字。

这些分隔符对于二进制和十六进制同样有用。

let bits = 0b0010_1010;
let routine = 0xC0FFEE_F00D_BED;
let martin = 0xF0_1E_

注意,可能有些反常识,js里的数字表示信用卡和电话号并不适当,这种情况下使用字符串更好。

当咱们将target设置为es2015编译的上述代码时,TypeScript 将生成以下js代码:

const worldPopulationIn2017 = 7600000000;
const leastSignificantByteMask = 255;
const papayawhipColorHexCode = 16773077;

in操作符细化和精确的 instanceof

TypeScript 2.7带来了两处类型细化方面的改动 - 通过执行“类型保护”确定更详细类型的能力。

首先,instanceof操作符现在利用继承链而非依赖于结构兼容性, 能更准确地反映出 instanceof操作符在运行时的行为。 这可以帮助避免一些复杂的问题,当使用 instanceof去细化结构上相似(但无关)的类型时。

其次,in操作符现在做为类型保护使用,会细化掉没有明确声明的属性名。

interface A { a: number };
interface B { b: string };

function foo(x: A | B) {
    if ("a" in x) {
        return x.a;
    }
    return x.b;
}

更智能的对象字面量推断

在 JS 里有一种模式,用户会忽略掉一些属性,稍后在使用的时候那些属性的值为undefined。

let foo = someTest ? { value: 42 } : {};

在以前TypeScript会查找{ value: number }和{}的最佳超类型,结果是{}。 这从技术角度上讲是正确的,但并不是很有用。

从2.7版本开始,TypeScript 会“规范化”每个对象字面量类型记录每个属性, 为每个undefined类型属性插入一个可选属性,并将它们联合起来。

在上例中,foo的最类型是{ value: number } | { value?: undefined }。 结合了 TypeScript 的细化类型,这让咱们可以编写更具表达性的代码且 TypeScript 也可理解。 看另外一个例子:

// Has type
//  | { a: boolean, aData: number, b?: undefined }
//  | { b: boolean, bData: string, a?: undefined }
let bar = Math.random() < 0.5 ?
    { a: true, aData: 100 } :
    { b: true, bData: "hello" };

if (bar.b) {
    // TypeScript now knows that 'bar' has the type
    //
    //   '{ b: boolean, bData: string, a?: undefined }'
    //
    // so it knows that 'bData' is available.
    bar.bData.toLowerCase()
}

这里,TypeScript 可以通过检查b属性来细化bar的类型,然后允许我们访问bData属性。

unique symbol 类型和常量名属性

TypeScript 2.7 对ECMAScript里的symbols有了更深入的了解,你可以更灵活地使用它们。

一个需求很大的用例是使用symbols来声明一个类型良好的属性。 比如,看下面的例子:

const Foo = Symbol("Foo");
const Bar = Symbol("Bar");

let x = {
    [Foo]: 100,
    [Bar]: "hello",
};

let a = x[Foo]; // has type 'number'
let b = x[Bar]; // has type 'string'

可以看到,TypeScript 可以追踪到x拥有使用符号Foo和Bar声明的属性,因为Foo和Bar被声明成常量。 TypeScript 利用了这一点,让Foo和Bar具有了一种新类型:unique symbols。

unique symbols是symbols的子类型,仅可通过调用Symbol()或Symbol.for()或由明确的类型注释生成。 它们仅出现在常量声明和只读的静态属性上,并且为了引用一个存在的unique symbols类型,你必须使用typeof操作符。 每个对unique symbols的引用都意味着一个完全唯一的声明身份。

// Works
declare const Foo: unique symbol;

// Error! 'Bar' isn't a constant.
let Bar: unique symbol = Symbol();

// Works - refers to a unique symbol, but its identity is tied to 'Foo'.
let Baz: typeof Foo = Foo;

// Also works.
class C {
    static readonly StaticSymbol: unique symbol = Symbol();
}

因为每个unique symbols都有个完全独立的身份,因此两个unique symbols类型之前不能赋值和比较。

const Foo = Symbol();
const Bar = Symbol();

// Error: can't compare two unique symbols.
if (Foo === Bar) {
    // ...
}

另一个可能的用例是使用 symbols做为联合标记。

// ./ShapeKind.ts
export const Circle = Symbol("circle");
export const Square = Symbol("square");

// ./ShapeFun.ts
import * as ShapeKind from "./ShapeKind";

interface Circle {
    kind: typeof ShapeKind.Circle;
    radius: number;
}

interface Square {
    kind: typeof ShapeKind.Square;
    sideLength: number;
}

function area(shape: Circle | Square) {
    if (shape.kind === ShapeKind.Circle) {
        // 'shape' has type 'Circle'
        return Math.PI * shape.radius ** 2;
    }
    // 'shape' has type 'Square'
    return shape.sideLength ** 2;
}

更严格的类属性检查

TypeScript 2.7 引入了一个新的编译器选项,用于类中严格的属性初始化检查。如果启用了--strictPropertyInitialization标志,则类型检查器将验证类中声明的每个实例属性

  • 是否有包含undefined的类型
  • 有一个明确的初始值设定项
  • 在构造函数中被明确赋值

--strictPropertyInitialization选项是编译器选项系列的一部分,当设置--strict标志时,该选项会自动启用。 与所有其他严格的编译器选项一样,咱们可以将--strict设置为true,并通过将--strictPropertyInitialization设置为false来有选择地退出严格的属性初始化检查。

请注意,必须设置--strictNullCheck标志(通过—strict直接或间接地设置),以便--strictPropertyInitialization起作用。

现在,来看看严格的属性初始化检查。如果没有启用--strictpropertyinitialized标志,下面的代码类型检查就可以了,但是会在运行时产生一个TypeError错误:

class User {
  username: string;
}

const user = new User();

// TypeError: Cannot read property 'toLowerCase' of undefined
const username = user.username.toLowerCase();

出现运行时错误的原因是,username属性值为undefined,因为没有对该属性的赋值。因此,对toLowerCase()方法的调用失败。

如果启用——strictpropertyinitialize,类型检查器将会报一个错误:

class User {
  // Type error: Property 'username' has no initializer
  // and is not definitely assigned in the constructor
  username: string;
}

接下来,看看四种不同的方法,可以正确地输入User类来消除类型错误。

解决方案1:允许定义

消除类型错误的一种方法是为username属性提供一个包含undefined的类型:

class User {
  username: string | undefined;
}

const user = new User();

现在,username属性保存undefined的值是完全有效的。但是,当咱们想要将username属性用作字符串时,首先必须确保它实际包含的是字符串而不是undefined的值,例如使用typeof

// OK
const username = typeof user.username === "string"
  ? user.username.toLowerCase()
  : "n/a";

解决方案2:显式属性初始化

消除类型错误的另一种方法是向username属性添加显式初始化。通过这种方式,属性将立即保存一个字符串值,并且不会明显的undefined:

class User {
  username = "n/a";
}

const user = new User();

// OK
const username = user.username.toLowerCase();

解决方案3: 使用构造函数赋值

也许最有用的解决方案是将username参数添加到构造函数中,然后将其分配给username属性。这样,每当构造User类的实例时,调用者必须提供用户名作为参数:

class User {
  username: string;

  constructor(username: string) {
    this.username = username;
  }
}

const user = new User("mariusschulz");

// OK
const username = user.username.toLowerCase();

咱们 还可以通过删除对类字段的显式赋值并将public修饰符添加到username构造函数参数来简化User类,如下所示:

class User {
  constructor(public username: string) {}
}

const user = new User("mariusschulz");

// OK
const username = user.username.toLowerCase();

请注意,严格的属性初始化要求在构造函数中所有可能的代码路径中明确分配每个属性。 因此,以下代码类型不正确,因为在某些情况下,我们将username属性赋值为未初始化状态:

class User {
  // Type error: Property 'username' has no initializer
  // and is not definitely assigned in the constructor.
  username: string;

  constructor(username: string) {
    if (Math.random() < 0.5) {
      this.username = username;
    }
  }
}

解决方案4:明确的赋值断言

如果类属性既没有显式初始化,也没有undefined的类型,则类型检查器要求直接在构造函数中初始化该属性;否则,严格的属性初始化检查将失败。如果咱们希望在帮助方法中初始化属性,或者让依赖项注入框架来初始化属性,那么这是有问题的。在这些情况下,咱们必须将一个明确的赋值断言(!)添加到该属性的声明中:

class User {
  username!: string;

  constructor(username: string) {
    this.initialize(username);
  }

  private initialize(username: string) {
    this.username = username;
  }
}

const user = new User("mariusschulz");

// OK
const username = user.username.toLowerCase();

通过向username属性添加一个明确的赋值断言,这会告诉类型检查器,期望对username属性进行初始化,即使它自己无法检测到这一点。现在咱们的责任是确保在构造函数返回后明确地将属性赋值给它,所以必须小心;否则,username属性可能被明显的undefined或者在运行时就会报TypeError错误。

显式赋值断言

尽管咱们尝试将类型系统做的更富表现力,但我们知道有时用户比TypeScript更加了解类型。

上面提到过,显式赋值断言是一个新语法,使用它来告诉 TypeScript 一个属性会被明确地赋值。 但是除了在类属性上使用它之外,在TypeScript 2.7里你还可以在变量声明上使用它!

let x!: number[];
initialize();
x.push(4);

function initialize() {
    x = [0, 1, 2, 3];
}

假设我们没有在x后面加上感叹号,那么TypeScript会报告x从未被初始化过。 它在延迟初始化或重新初始化的场景下很方便使用。

以上就是详解TS数字分隔符和更严格的类属性检查的详细内容,更多关于TS的资料请关注Devmax其它相关文章!

详解TS数字分隔符和更严格的类属性检查的更多相关文章

  1. ios – 覆盖Swift中的超类委托

    我正在开发一个包含两个UIViews的Swift(v1.2)项目.MyView和MyViewSubclass.MyView有一个委托,我想在MyViewSubclass中覆盖它作为一个子协议,类似于UITableViews有一个UITableViewDelegate,它也符合超级uiscrollviewdelegate.我的第一个想法是覆盖超类属性,但这会导致编译器错误,因为子类不能覆盖具有不同类

  2. Swift多种实现单例写法

    学习Swift的单例写法,以下提供了5种方式,一起来看看吧!

  3. Swift Selector

    看了喵神的Swifter100个必备的小tips总结下swift中不支持@selector关键字,将SEL重新定义为结构体了。

  4. 12-Swift中的类

    1类的介绍Swift也是一门面向对象开发的语言面向对象的基础是类,类产生了对象在Swift中如何定义类呢?该参数名默认为newValue,我们可以自己定义该参数名didSet:在新属性值被存储后立即调用。与willSet相同,此时传入的是属性的旧值,默认参数名为oldValuewillSet与didSet只有在属性第一次被设置时才会调用,在初始化时,不会去调用这些监听方法监听的方式如下:监听age和name的变化

  5. Swift——类的基本属性

  6. 关于在Swift中重写类属性的困惑

    另外,我应该如何从实例方法中访问属性的值,并且每次都获得正确的值?以下代码是否有效?你非常接近于那里,除了你不能覆盖子类中的静态属性–这就是静态的意思.所以你必须使用类属性,这意味着它必须是一个计算属性–Swift缺少存储的类属性.所以:让我们测试一下:

  7. 使用sockets:从新闻组中获取文章(三)

    >我们从服务器的这个新闻组中读取了最后的十篇文章,。也可以通过使用HEAD命令读取文章的头信息,或者使用BODY命令读取文章内容。>关于fclose()的更多信息,请参考http://www.php.net/manual/function.fclose.php结论在上文中,我们看到了怎样打开、使用然后关闭一个socket:连接到一个NNTP服务器,取回一些文章。使用POST命令发表文章也复杂不到哪儿去。下一步就是编写一个基于WEB的新闻组客户端了。这样,你有了一个基于web的搜索新闻组的程序了。

  8. Typescript中extends关键字的基本使用

    extends表示具体的泛型类型只能是object类型,某个变量如果能断言成object类型[变量as object],那么这个变量的类型符合T extends object,下面这篇文章主要给大家介绍了关于Typescript中extends关键字基本使用的相关资料,需要的朋友可以参考下

  9. JavaScript类属性的访问方式详解

    这篇文章主要介绍了JavaScript类属性的访问方式,需要的朋友可以参考下

  10. JDBCTM 指南:入门5 - ResultSet

    JavaSoft目前正在准备这本书。ResultSet.next方法用于移动到ResultSet中的下一行,使下一行成为当前行。在ResultSet对象或其父辈Statement对象关闭之前,光标一直保持有效。关于ResultSet中列的信息,可通过调用方法ResultSet.getMetaData得到。返回的ResultSetMetaData对象将给出其ResultSet对象各列的编号、类型和属性。getString的返回值将为JavaString对象。TIYITSMALLITITEGERBIGNTRE

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部