zoukankan      html  css  js  c++  java
  • TypeScript入门教程-阮一峰

    从 JavaScript 程序员的角度总结思考,循序渐进的理解 TypeScript。

    http://ts.xcatliu.com/

    什么是TypeScript?

    TypeScript是一种添加了类型系统的 JavaScript,适用于任何规模的项目。

    我们都知道JavaScript是一种弱类型的语言。而TypeScript增强了它的类型。

    由于JavaScript 是一门非常灵活的编程语言,这将导致:

    • 它没有类型约束,一个变量可能初始化时是字符串,过一会儿又被赋值为数字。
    • 由于隐式类型转换的存在,有的变量的类型很难在运行前就确定。
    • 基于原型的面向对象编程,使得原型上的属性或方法可以在运行时被修改。
    • 函数是 JavaScript 中的一等公民[2],可以赋值给变量,也可以当作参数或返回值。

    这种灵活性就像一把双刃剑,一方面使得 JavaScript 蓬勃发展,无所不能,从 2013 年开始就一直蝉联最普遍使用的编程语言排行榜冠军;另一方面也使得它的代码质量参差不齐,维护成本高,运行时错误多。

    而 TypeScript 的类型系统,在很大程度上弥补了 JavaScript 的缺点。它是静态语言。

    而JavaScript是动态语言,如下面的代码直到运行时才会报错:

    let foo = 1;
    foo.split(' ');

    同样是这段代码,在TypeScript下编译时就会报错了。

    let foo = 1;
    foo.split(' ');

    你可能会奇怪,这段 TypeScript 代码看上去和 JavaScript 没有什么区别呀。没错!大部分 JavaScript 代码都只需要经过少量的修改(或者完全不用修改)就变成 TypeScript 代码,这得益于 TypeScript 强大的[类型推论][],即使不去手动声明变量 foo 的类型,也能在变量初始化时自动推论出它是一个 number 类型。

    完整的 TypeScript 代码是这样的:

    let foo: number = 1;
    foo.split(' ');

    TypeScript 是弱类型:

    类型系统按照「是否允许隐式类型转换」来分类,可以分为强类型和弱类型。

    以下这段代码不管是在 JavaScript 中还是在 TypeScript 中都是可以正常运行的,运行时数字 1 会被隐式类型转换为字符串 '1',加号 + 被识别为字符串拼接,所以打印出结果是字符串 '11'

    console.log(1 + '1');

    作为对比,Python是强类型的。需要进行强制类型转换。

    这样的类型系统体现了 TypeScript 的核心设计理念:在完整保留 JavaScript 运行时行为的基础上,通过引入静态类型系统来提高代码的可维护性,减少可能出现的 bug。

    TypeScript非常适合于大型项目。在中小型项目中推行 TypeScript 的最大障碍就是认为使用 TypeScript 需要写额外的代码,降低开发效率。但事实上,由于有[类型推论][],大部分类型都不需要手动声明了。TypeScript 还可以和 JavaScript 共存。这意味着如果你有一个使用 JavaScript 开发的旧项目,又想使用 TypeScript 的特性,那么你不需要急着把整个项目都迁移到 TypeScript,你可以使用 TypeScript 编写新文件,然后在后续更迭中逐步迁移旧文件。如果一些 JavaScript 文件的迁移成本太高,TypeScript 也提供了一个方案,可以让你在不修改 JavaScript 文件的前提下,编写一个[类型声明文件][],实现旧项目的渐进式迁移。

    事实上,就算你从来没学习过 TypeScript,你也可能已经在不知不觉中使用到了 TypeScript——在 VSCode 编辑器中编写 JavaScript 时,代码补全和接口提示等功能就是通过 TypeScript Language Service 实现的。

    一些第三方库原生支持了 TypeScript,在使用时就能获得代码补全了,比如 Vue 3.0

    有一些第三方库原生不支持 TypeScript,但是可以通过安装社区维护的类型声明库(比如通过运行 npm install --save-dev @types/react 来安装 React 的类型声明库)来获得代码补全能力——不管是在 JavaScript 项目中还是在 TypeScript 中项目中都是支持的:

    • 2016-05:@types/react 发布,TypeScript 可以开发 React 应用了。
    • 2016-05:@types/node 发布,TypeScript 可以开发 Node.js 应用了。
    • 2020-09:Vue 发布了 3.0 版本,官方支持 TypeScript。

    安装TypeScript

    TypeScript 的命令行工具安装方法如下:

    npm install -g typescript

    以上命令会在全局环境下安装 tsc 命令,安装完成之后,我们就可以在任何地方执行 tsc 命令了。

    编译一个 TypeScript 文件很简单:

    tsc hello.ts

    我们约定使用 TypeScript 编写的文件以 .ts 为后缀,用 TypeScript 编写 React 时,以 .tsx 为后缀。

    Hello TypeScript

    我们从一个简单的例子开始。

    将以下代码复制到 hello.ts 中:

    function sayHello(person: string) {
        return 'Hello, ' + person;
    }
    
    let user = 'Tom';
    console.log(sayHello(user));

    然后执行

    tsc hello.ts

    这时候会生成一个编译好的文件 hello.js

    function sayHello(person) {
        return 'Hello, ' + person;
    }
    var user = 'Tom';
    console.log(sayHello(user));

    上述例子中,我们用 : 指定 person 参数类型为 string。但是编译为 js 之后,并没有什么检查的代码被插入进来。

    这是因为 TypeScript 只会在编译时对类型进行静态检查,如果发现有错误,编译的时候就会报错。而在运行时,与普通的 JavaScript 文件一样,不会对类型进行检查。

    如果我们需要保证运行时的参数类型,还是得手动对类型进行判断:

    function sayHello(person: string) {
        if (typeof person === 'string') {
            return 'Hello, ' + person;
        } else {
            throw new Error('person is not a string');
        }
    }
    
    let user = 'Tom';
    console.log(sayHello(user));

    let 是 ES6 中的关键字,和 var 类似,用于定义一个局部变量,可以参阅 let 和 const 命令

    下面尝试把这段代码编译一下:

    function sayHello(person: string) {
        return 'Hello, ' + person;
    }
    
    let user = [0, 1, 2];
    console.log(sayHello(user));

    编辑器中会提示错误,编译的时候也会出错。

    但是还是生成了 js 文件。

    这是因为 TypeScript 编译的时候即使报错了,还是会生成编译结果,我们仍然可以使用这个编译之后的文件。

    如果要在报错的时候终止 js 文件的生成,可以在 tsconfig.json 中配置 noEmitOnError 即可。关于 tsconfig.json,请参阅官方手册中文版)。

    基础

    本部分介绍了 TypeScript 中的常用类型和一些基本概念,旨在让大家对 TypeScript 有个初步的理解。具体内容包括:

    原始数据类型

    JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。 原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol 和 ES10 中的新类型 BigInt。 本节主要介绍前五种原始数据类型在 TypeScript 中的应用。

    布尔值

    数值

    字符串

    空值

    Null 和 Undefined

    任意值

    任意值(Any)用来表示允许赋值为任意类型。

    什么是任意值类型?

    如果是一个普通类型,在赋值过程中改变类型是不被允许的。但如果是 any 类型,则允许被赋值为任意类型。

    任意值的属性和方法:

    在任意值上访问任何属性都是允许的。

    也允许调用任何方法。

    可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。

    未声明类型的变量

    变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型。

    类型推论

    如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。

    什么是类型推论

    以下代码虽然没有指定类型,但是会在编译的时候报错:

    let myFavoriteNumber = 'seven';
    myFavoriteNumber = 7;

    TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。

    联合类型

    联合类型(Union Types)表示取值可以为多种类型中的一种。

    简单的例子

    let myFavoriteNumber: string | number;
    myFavoriteNumber = 'seven';
    myFavoriteNumber = 7;

    访问联合类型的属性或方法

    对象的类型——接口

    在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。

    什么是接口

    在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。

    TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

    简单的例子

    interface Person {
        name: string;
        age: number;
    }
    
    let tom: Person = {
        name: 'Tom',
        age: 25
    };

    上面的例子中,我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。

    可选属性

    有时我们希望不要完全匹配一个形状,那么可以用可选属性。

    任意属性

    有时候我们希望一个接口允许有任意的属性,可以使用如下方式。

    interface Person {
        name: string;
        age?: number;
        [propName: string]: any;
    }

    只读属性

    有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性。

    数组的类型

    在 TypeScript 中,数组类型有多种定义方式,比较灵活。

    「类型 + 方括号」表示法

    数组泛型

    用接口表示数组

    接口也可以用来描述数组:

    interface NumberArray {
        [index: number]: number;
    }
    let fibonacci: NumberArray = [1, 1, 2, 3, 5];

    NumberArray 表示:只要索引的类型是数字时,那么值的类型必须是数字。

    虽然接口也可以用来描述数组,但是我们一般不会这么做,因为这种方式比前两种方式复杂多了。

    不过有一种情况例外,那就是它常用来表示类数组。

    类数组

    类数组(Array-like Object)不是数组类型,比如 arguments

    function sum() {
        let args: number[] = arguments;
    }

    上例中,arguments 实际上是一个类数组,不能用普通的数组的方式来描述,而应该用接口:

    function sum() {
        let args: {
            [index: number]: number;
            length: number;
            callee: Function;
        } = arguments;
    }

    在这个例子中,我们除了约束当索引的类型是数字时,值的类型必须是数字之外,也约束了它还有 length 和 callee 两个属性。

    事实上常用的类数组都有自己的接口定义,如 IArgumentsNodeListHTMLCollection 等:

    关于内置对象,可以参考内置对象一章。

  • 相关阅读:
    gdb 查看变量~p长串末尾省略号, 一个页面显示不完
    Git 在团队中的最佳实践--如何正确使用Git Flow[转]
    apktool+dex2jar+xjad反编译android程序
    浏览器缓存详解:expires,cache-control,last-modified,etag详细说明
    64位windows 7下成功配置TortoiseGit使用Github服务器
    github简单使用教程
    浅淡HTML5移动Web开发
    Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系
    深入浅出Symfony2
    利用PHP取二进制文件头判断文件类型
  • 原文地址:https://www.cnblogs.com/2008nmj/p/14943063.html
Copyright © 2011-2022 走看看