为了账号安全,请及时绑定邮箱和手机立即绑定

Announcing TypeScript 2.8

标签:
JavaScript

TypeScript 2.8 在这里,我们带来一些你会十分喜爱的功能!

你熟悉 TypeScript 吗? 它是一种将可选的静态类型添加到JavaScript的语言。 这些静态类型会让你的代码不犯拼写错误以及其他的愚蠢错误。 基于围绕这些类型所构建的组件,还可以实现代码自动补全和项目索引等更多美妙的功能。 把你的代码交给 TypeScript 编译器运行后,将只剩下清晰,可读性强且符合标准的JavaScript代码。 也可以将你的代码重写为大部分仅支持 es5 甚至 es3 旧版浏览器所能运行的代码。想要了解 TypeScript 的更多相关信息 , 请查看我们的文档.

如果你现在急不可待,你可以通过 NuGet 下载或者运行以下指令:

npm install -g typescript

你能获得以下编辑器支持:

其他编辑器 的支持更新时间可能会不同, 但会尽快提供。

为了快速浏览我们在此版本中发布的内容,我们将其组合成列表放在一起便于浏览:

如果你升级版本,你需要记住一些细微部分的 调整 .

除此之外,让我们来看看 TypeScript 2.8带来的新功能吧!!

条件类型 (Conditional types)

条件类型是 TypeScript 中的一个新构造,它允许我们根据其他类型来选择类型。 它们的表现形式是:

A extends B ? C : D

其中a,b,c和d是所有类型。 你应将其看作 " 当类型a可赋值给b时,那么这种类型是c; 否则,它是d。" 如果你曾在JavaScript中使用过条件语法,你将会感到很熟悉。

我们举两个具体的例子:

interface Animal {
    live(): void;
}interface Dog extends Animal {
    woof(): void;
}// 类型是 'number'type Foo = Dog extends Animal ? number : string;// 类型是 'string'type Bar = RegExp extends Dog ? number : string;

你可能会想知道改变为什么会立即生效。我们可以知道 Foo 是数字,而 Bar 会是字符串,所以我们不妨明确地写出它。但条件类型的真正强大之处来自于它们与泛型的使用。

例如,让我们来看看以下几个方法:

interface Id { id: number, /* other fields */ }interface Name { name: string, /* other fields */ }declare function createLabel(id: number): Id;declare function createLabel(name: string): Name;declare function createLabel(name: string | number): Id | Name;

这些用于createlabel的重载,向我们展示了一个根据其输入类型作出选择的单个javascript函数。请注意以下两点:

  1. 如果一个资源必须在整个api中反复进行同样的选择,这会变得很麻烦。

  2. 我们必须创建三个重载:一个用于确定类型的每个案例,另一个用于最常见的案例。 对于我们必须处理的其他情况,重载次数会呈指数级增长。

相反,我们可以使用条件类型将我们的两个重载忽略到一个,并创建一个类型别名,以便我们可以重用该逻辑。

type IdOrName<T extends number | string>=T extends number ? Id : Name;
declare function createLabel<T extends number | string>(idOrName: T): T extends number ? Id : Name;
let a = createLabel("typescript");// Namelet b = createLabel(2.8);// Idlet c = createLabel("" as any);// Id | Namelet d = createLabel("" as never);// never

如同JavaScript可以根据一个值的特性在运行时做出决定一般,条件类型让 TypeScript 根据其他类型的特征在类型系统中作出判断。

作为另一个例子,我们也可以编写一个名为flatten的类型,将数组类型平移为它们的元素类型,但是另外保留它们:

// 如果我们有一个数组,当我们用 'number' 索引时,将会得到这个类型。
// 否则,单独留下类型。type Flatten`<T>`
= T extends any[] ? T[number] : T;

在条件类型内的推断

条件类型也为我们提供了一种方法,通过使用 infer 关键字从我们在真正分支中比较的类型推断出来。例如,我们可以在flatten中推断出元素类型,而不用去动手取出它

// 我们可以使用 '(infer U)[]' 而不是 'Array`<infer U>`'type Flatten`<T>`
= T extends Array`<infer U>`
? U : T;

在这里,我们已经声明式地引入了一个名为u 的新的泛型类型变量,而不是指定如何检索 t的元素类型。这使我们不再考虑如何获取想要的类型。

通过条件分配给联合体( unions )

当条件类型对单个类型参数起作用时,它们分布在各个联合体中。 所以在下面的例子中,Bar的类型是 string [] | number [] 因为Foo应用于联合类型string |number

type Foo`<T>`= T extends any ? T[] : never;
/**
 * Foo distributes on 'string | number' to the type
 *
 *    (string extends any ? string[] : never) |
 *    (number extends any ? number[] : never)
 * 
 * which boils down to
 *
 *    string[] | number[]
 */
type Bar = Foo<string | number>;

如果你想要避免在联合体(unions)中发布内容,则可以使用中括号括住“extends”关键字的两侧:

type Foo`<T>` = [T] extends [any] ? T[] : never;// Boils down to Array<string | number>type Bar = Foo<string | number>;

虽然条件类型起初可能看起来有点吓人,但当你需要进一步推动类型系统以获得准确类型时,我们相信它们将为你带来大量的灵活性。

新的内置助手

TypeScript 2.8 在lib.d.ts中提供了几种利用条件类型的新类型别名:

// 这些都已经内置到lib.d.ts中!/**
 * Exclude from T those types that are assignable to U
 */type Exclude`<T, U>` = T extends U ? never : T;/**
 * Extract from T those types that are assignable to U
 */type Extract`<T, U>` = T extends U ? T : never;/**
 * Exclude null and undefined from T
 */type NonNullable`<T>` = T extends null | undefined ? never : T;/**
 * Obtain the return type of a function type
 */type ReturnType`<T extends (...args: any[]) =>` any> = T extends (...args: any[]) => infer R ? R : any;/**
 * Obtain the return type of a constructor function type
 */type InstanceType`<T extends new (...args: any[]) =>` any> = T extends new (...args: any[]) => infer R ? R : any;

NonnullableReturntypeInstancetype相对不言而喻,ExcludeExtract则更有趣一些。

Extract 从第一个参数中选择用于给第二个参数分配的类型:

// string[] | number[]type Foo = Extract<boolean | string[] | number[], any[]>;

Exclude 则相反,它从第一个参数中删除不能分配给第二个参数的类型 :

// booleantype Bar = Exclude<boolean | string[] | number[], any[]>;

只有声明才会发出

感谢来自Manoj Patel一个pull请求,TypeScript 现在提供了一个--emitDeclarationOnly标志,当你有一个发送JavaScript文件的替代构建步骤时,可以使用这个标志,但需要单独发出声明文件。 在这种模式下,不会生成JavaScript文件和源代码文件;只是可以用于资源使用者的.d.ts文件。

其中一个用例就是使用备用编译器来处理TypeScript,例如babel 7.对于利用此标志的存储库示例,check out urql from Formidable Labs,或查看our Babel starter repo

@jsx 预处理指令(pragma comments)

通常,jsx的用户希望将其jsx标记重写为React.createElement。然而,如果你使用的库有类似React的工厂api,比如Preact, Stencil, Inferno, Cycle其他等,你可能想稍微调整一下。

曾经,TypeScript 只允许用户使用jsxFactory选项(以及不赞成使用的reactNamespace选项)在全局级别控制jsx的发送。但如果你想在同一个应用程序中混合使用这些库,那 jsx 将无法使用。

幸运的是,TypeScript 2.8现在允许你通过在文件顶部添加一个// @ jsx注释来逐个文件地设置你的jsx工厂。如果你在babel中使用了相同的功能,这应该看起来有些熟悉。

/** @jsx dom */import { dom } from "./renderer"<h></h>

上面的示例导入了一个名为dom的函数,并使用jsx编译指示选择dom作为文件中所有jsx表达式的工厂。TypeScript 2.8 在编译为commonjs和es5时会将其重写为以下内容:

var renderer_1 = require("./renderer");
renderer_1.dom("h", null);

JSX 通过jsx工厂解决

目前,当typescript使用jsx时,它会查找全局JSX命名空间来查找某些类型(例如“什么是jsx组件的类型?”)。在 TypeScript 2.8中,编译器将尝试根据jsx工厂的位置查找JSX命名空间。例如,如果您的jsx工厂是React.createElement,那么 TypeScript 将尝试首先解析React.JSX,然后解析当前范围内的JSX

当混合和匹配不同库(例如: React 和 Preact)或特定库的不同版本(例如: React14和React16)时,这可能是有用的,因为将jsx名称空间放置在全局范围中可能导致问题。

今后,我们建议新的面向jsx的库避免将JSX放在全局范围内,而是将它从相应的工厂函数的相同位置导出。然而,为了能够向后兼容,TypeScript 将在必要时继续回落到全局范围。

精确地控制映射类型的修饰符

TypeScript 的映射对象类型是一个非常强大的构造。它有个便利的功能:是允许用户创建新的类型,其中包含为其所有属性设置的修饰符。例如,以下类型创建一个基于T的新类型,并且T中的每个属性都变为只读和可选(?)。

// 创建一个包含 T 中所有属性的类型,,
// 但标记为只读和可选.type ReadonlyAndPartial`<T>` = {
    readonly [P in keyof T]?: T[P]
}

所以映射的对象类型可以添加修饰符,但直到此时,无法从T删除remove修饰符。

TypeScript 2.8提供了用-运算符去除映射类型中的修饰符的新语法,以及用+运算符添加修饰符的新的更加明确的语法。例如:

type Mutable`<T>` = {
    -readonly [P in keyof T]: T[P]
}interface Foo {
    readonly abc: number;
    def?: string;
}// 'abc'is no longer read-only, but 'def'is still optional.type TotallyMutableFoo = Mutable`<Foo>`

在上面的例子中,Mutable从它映射的类型的每个属性中删除readonly。 同样,TypeScript 现在在lib.d.ts中提供了一个新的Required类型,用于从每个属性中删除选项:

/**
 * Make all properties in T required
 */type Required`<T>` = {
    [P in keyof T]-?: T[P];
}

当你想调出一个映射类型添加修饰符时,+操作符可以很方便的实现。例如,我们从上面的ReadonlyAndPartial可以定义如下:

type ReadonlyAndPartial`<T>` = {
    +readonly [P in keyof T]+?: T[P];
}

组织导入(Organize imports)

TypeScript 的语言服务现在提供了组织导入的功能。此功能将删除所有未使用的导入,按文件路径对现有导入进行排序,并对命名导入进行排序。

修复未初始化的属性

TypeScript 2.7引入了对类中未初始化属性的额外检查,感谢来自Wenlu Wanga pull request。 TypeScript 2.8带来了一些有用的快速修复,使其更容易添加到您的代码库中。

突破性的改变

--noUnusedParameters下检查未使用的类型参数

未使用的类型参数先前在--noUnusedLocals下报告过,但是现在报告在--noUnusedParameters下。

HTMLObjectElement 不再具有 alt 属性

这种行为不包含在 WHATWG 标准中。

下一步是什么?

我们希望 TypeScript 2.8能够进一步推动 envelope 提供一种真正代表 JavaScript 作为语言本质的类型系统。因此,我们相信我们可以在你编码的过程中,为你创造出一种更高效、更快乐的体验。

在接下来的几周内,我们将更清楚地了解 TypeScript 2.9的存储情况,但是和往常一样,你可以留意 TypeScript 路线图 ,看看我们正在为下一个版本做些什么。你也可以尝试我们的夜间发布,今天就能体验未来!例如,通用的jsx元素已经出现在 TypeScript 最近的夜间版本中!

让我们知道你对这个版本的看法,请写在Twitter 上或者在下面的评论中, 并随时向我们提交建议和 a GitHub issue.

可劲儿造吧!(Happy Hacking!)

原文出处

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消