什么是打字稿?

TypeScript 由 Microsoft 创建和维护,是 JavaScript 的超集,这意味着所有功能性 JavaScript 代码在 TypeScript 下都是有效的。该语言可以理解为“用于应用程序级开发的 JavaScript”,主要关注两个方面:

  • 提供从未来 JavaScript 引擎到当前 JavaScript 引擎的功能
  • 为 JavaScript 提供类型系统

TypeScript 的组件通常是语言本身,本质上是 JavaScript 加上附加的功能和语法,将代码转换为 JavaScript 的编译器,以及在编译器管道末端提供类似编辑器的应用程序的语言服务。

那么,为什么要使用 TypeScript?

  • 打字: TypeScript 提供静态打字,微软和谷歌等许多大型团队发现这有助于简化开发过程。
  • 面向对象编程: TypeScript 支持面向对象的编程概念,如接口、继承、类等。
  • **编译:**与解释性语言 JavaScript 不同,TypeScript 会为您编译代码并查找编译错误,从而更容易调试。

img

安装 TypeScript

在我们深入研究 TypeScript 之前,请确保您已成功安装 TypeScript。获取 TypeScript 工具的两种主要方法是通过 npm(Node.js 包管理器)或安装 TypeScript 的 Visual Studio 插件。

NPM:

安装

> npm install -g typescript

编译

> tsc helloworld.ts

TypeScript 的优点和局限性

TypeScript

JavaScript 是一种动态类型语言,这意味着类型错误仅在运行时才被发现。对于从事复杂项目的大型团队来说,这可能是一个显着的缺点,因为事先发现代码的所有错误会容易得多。

TypeScript 提供可选的静态类型,因此变量不能更改其类型并且只能接受某些值。这种类型有助于 TypeScript 编译器发现更多错误,以便开发人员使用不易出错的代码。类型保护通过使其更具可读性和更容易重构来为代码创建更多结构。

IDE 支持

因为 TypeScript 提供了类型,所以文本编辑器和集成开发环境 (IDE) 可以为开发人员提供更多有用的信息。这些环境可以提供自动完成、代码导航、错误标记等功能,以提高团队的生产力。

一些支持 TypeScript 3 的流行环境:

  • Microsoft Visual Studio
  • WebStorm
  • Visual Studio Code
  • Atom
  • Eclipse

浏览器兼容性

浏览器兼容性是 TypeScript 提供的强大功能之一。TypeScript 编译器会转换您的代码,使其与所有现代浏览器兼容。这种兼容性是因为编译器能够将 TypeScript 代码翻译成所有设备、平台和浏览器都支持的 vanilla JS。

尽管使用 TypeScript 有很多优点,但它并不是一个完美的解决方案。提高代码可读性的一个缺点是您必须编写更多代码,这可能会增加您的开发时间。与使用原生 JavaScript 相比,它还增加了 TypeScript 文件的大小。

严格类型简介

现在我们已经了解了 TypeScript 所提供的功能,让我们深入了解一些使 TypeScript 成为强大工具的更高级的概念。

noImplicitAny

根据文档, 的定义noImplicitAny是“在任何隐含的任何类型的表达式和声明上引发错误”。

这意味着只要 TypeScript 可以推断类型,如果您允许noImplicitAny. 这个例子可以通过传递函数参数来看到。

1
2
3
4
5
6
function print(arg) {
    send(arg);
}

print("hello");
print(4);

在上面的代码中,函数**的有效参数是print什么?**如果你没有为函数参数添加类型,TypeScript 将分配 type 的参数any,这将关闭类型检查。

对于喜欢代码安全的开发人员,他们可以使用noImplicityAny,这将通知他们在代码中键入的任何可能性any。让我们看看使用相同的print功能会发生什么。

function print(arg) { // Error : someArg has an implicit `any` type
    send(arg);
}

要修复错误,您可以注释函数参数。

function print(arg: number) { // Error : someArg has an implicit `any` type
    send(arg);
}

但是如果你仍然想要类型any,你可以显式地将参数标记为any

function print(arg: any) { // Error : someArg has an implicit `any` type
    send(arg);
}

unknown

unknown类型与类型相似之处在于any所有类型都可分配给anyandunknown类型,但区别在于该any类型可分配给任何其他类型,但该unknown类型不可分配给任何其他类型。区别可能是一个令人困惑的概念,所以让我们看一个例子。

function example1(arg: any) {
  const a: str = arg; // no error
  const b: num = arg; // no error
}

function example2(arg: unknown) {
  const a: str = arg; // 🔴 Type 'unknown' is not assignable to type 'string'.(2322)
  const b: num = arg; // 🔴 Type 'unknown' is not assignable to type 'number'.(2322)
}

一个变量arg被传递给这两个函数,它们的类型可以是stringnumber或其他类型。无论其类型如何,arg都会被分配类型anyunknown

但是,与any类型不同,类型的变量unknown不能被分配给另一个类型,如第 7 行和第 8 行所示。any类型是双向的,而是unknown单向的。

unknown在您不知道要传递给函数的值的类型但想摆脱这种情况的情况下,该类型会很有帮助any。这增加了代码的安全性,因为any类型可以传播,使您的代码库更容易出错。

strictNullChecks

在 TypeScript 中,null并且undefined可以分配给每种类型,这意味着它们在所有类型的域中。

let num: number = 123;
num = null; // Okay
num = undefined; // Okay

通常,这可能会导致意外错误,因为您可以在值为null或的变量上调用方法undefined

interface Person {
  hello(): void;
}

const num: number = undefined;
const str: string = null;
const person: Person = null;

person.hello(); // 🔴 Runtime Error!

在严格的空检查模式下,null不会undefined自动属于所有类型,因此您不能将它们用于不包含nullor的类型undefined。这样,您可以在编译时得到一个错误,即Object is possibly 'undefined'.

TypeScript 和面向对象的编程

课程

就像在 ES6 中一样,可以使用class关键字来实现类。要创建一个对象,一个类的实例,可以使用new关键字,它会调用构造函数。请参见下面的示例:

Luna是 的实例对象Dog

class Dog
{
    age: number
    breed: string    
    
    constructor(age: number, breed: string) 
    {
        this.age = age
        this.breed = string
    }    
    
    getRelativeAge(): number
    {
        return this.age * 7
    }
}

let Luna = new Dog(2, 'Labrador')

这种语法等同于在 JavaScript ES5 中使用函数对象。

function Dog(age, breed)
{
    this.age = age
    this.breed = breed
}

Dog.prototype.getRelativeAge = function() {
    return this.age * 7
}

var Spot = new Dog(2, 'Labrador')

继承

既然您知道如何创建对象,那么了解 TypeScript 中的继承就很重要了。继承允许子类从其父类继承某些属性。

例如,您有Animal, 作为父类。

class Animal
{
    age: number
    breed: string    
    
    constructor(age: number, breed: string)
    { 
        this.age = age
        this.breed = breed
    }    
    
    makeSound_(sound: string): void
    {
        console.log(sound)
        console.log(sound)
        console.log(sound)
    }
}

然后,您可以创建一个Dog子类。可以使用super关键字实现基本的继承,在子类中作为函数调用对应的父函数。

class Dog extends Animal
{
    playsFetch: boolean    constructor(age: number, breed: string, playsFetch: boolean)
    {
         super(age, breed) // call parent constructor
         this.playsFetch = playsFetch
    }    makeSound(): void
    {
        super.makeSound_('woof woof')
    }    getAgeInHumanYears(): number
    {
        return this.age * 7    // super.age will throw error
    }
}
class Cat extends Animal
{
    constructor(age: number, breed: string)
    {
        super(age, breed)
    }    makeSound(): void
    {
        super.makeSound_('meow meow')
    }
}

接口

接口在 JavaScript(和 TypeScript)中非常强大,因为它们对运行时的影响为零。TypeScript 允许你声明变量的结构,这给了你更多的权力。

interface Point {
    x: number; y: number;
}
declare var test: Point;

TypeScript 中的接口是开放式的,因此其他作者可以在现有的test变量声明的基础上进行构建。

interface Point {
    x: number; y: number;
}
declare var myPoint: Point;

interface Point {
    z: number;
}

var myPoint.z; // Allowed

类还可以实现接口,以便它们使用implements关键字遵循预定义的对象结构。

interface Point {
    x: number; y: number;
}

class MyPoint implements Point {
    x: number; y: number; // Same as Point
}

由于这个implements关键字,界面中的任何更改都会产生编译错误,以便您可以轻松更新代码库。

interface Point {
    x: number; y: number;
    z: number; // New member
}

class MyPoint implements Point { // ERROR : missing member `z`
    x: number; y: number;
}

TypeScript 中的类型

TypeScript 最重要的方面之一是从现有的泛型类型创建自定义类型。

联合类型

通常,您可能希望您的代码允许一种以上的数据类型。在接受nullorundefined值时,这种需求尤其明显。联合类型可以解决这个问题,用|注解表示。

const hello = (name: string | undefined) => { /* ... */ };

在此示例中,类型name定义为string | undefined,这意味着任何类型的变量name都可以是 astringundefined

路口类型

交集类型将多种类型合二为一,使得新类型具有组合类型的特征。您可以通过extend关键字执行此操作,如下所示。

function extend<T, U>(first: T, second: U): T & U {
  return { ...first, ...second };
}

const x = extend({ a: "hello" }, { b: 42 });

// x now has both `a` and `b`
const a = x.a;
const b = x.b;

元组类型

与 JavaScript 不同,TypeScript 提供了 Tuple 类型,它允许您使用非统一类型和固定数量的元素来表示数组。下面的示例演示了一个元组。

var nameNumber: [string, number];

// Okay
nameNumber = ['Ben', 12345];

// Error
nameNumber = ['Ben', '12345'];

其他要学习的主题

要成为真正的 TypeScript 大师,还有很多东西要学。看看这个列表,看看未来会发生什么。

  • Mapped types
  • Discriminated union types
  • Decorators
  • Function types and return types
  • Functional programming in TypeScript
  • State machines
  • Generic functions
  • Other type guards