阅读 243

TypeScript学习笔记(一)

TypeScript学习笔记(一)包括 TS介绍TS安装TS类型注释TS定义接口TS定义类TS基础类型

初识TS

TS由来

1.TypeScript介绍

看了一些TS的介绍,总结起来:这个东西和JavaScript的语法差不多,但是要更(bu)厉(hao)害(xie)一些。为解决JavaScript语言遗留的一些问题而生,达到降低bug率、使代码更易于维护等目的。

TypeScript个人简历

  • JavaScript的超集。
  • 支持ECMAScript 6标准,并支持输出ECMAScript 3/5/6标准的纯JavaScript代码。
  • 支持ECMAScript未来提案中的特性,比如异步功能和装饰器。
  • 支持类型系统且拥有类型推断。
  • 支持运行在任何浏览器、Node.js环境中。

2.相关阅读

Angular团队在更新Angular 2时开始全面采用TypeScript代码。他们给出了这样两个理由:

① TypeScript明确了抽象。

在大型工程项目中,我们希望模块之间的边界是使用接口定义的,而JavaScript不足以清晰表达类似的边界划分,Flow也不能(Flow指:2014年,Facebook推出了flow来对JavaScript进行类型检查)。而TypeScript可以定义接口,可以强制程序员去思考 API 的边界,去设计代码,而不只是编写代码,暴露代码的耦合。

② TypeScript可以使代码在一定程度上达到“Self-documenting”的效果。

“Self-documenting”是一个非常有意思的概念,它强调的是代码本身具有自我说明的效果,而不是依赖文档。TypeScript有着非常严格的强类型表达,这迫使你在函数使用之前就必须标注好函数的入参和返回值类型。这样的强依赖使得函数本身表达清晰,同时也可以非常容易地推导出代码的依赖结构,进行重构。

TS特点

1)免费开源,使用Apache授权协议。
2)基于ECMAScript标准进行拓展,是JavaScript的超集。
3)添加了可选静态类型、类和模块。
4)可以编译为可读的、符合ECMAScript规范的JavaScript。
5)成为一款跨平台的工具,支持所有的浏览器、主机和操作系统。
6)保证可以与JavaScript代码一起运行,无须修改。(这一点保证了JavaScript项目可以向TypeScript平滑迁移。)
7)文件拓展名是ts。
8)编译时检查,不污染运行时。

TS的安装

1.采用npm的方式

这种方式需要在NodeJS环境下,如果你已经安装了NodeJS,输入全局安装指令即可

npm install -g typescript
复制代码

2.检查安装是否完成,输入指令,查看安装版本号

tsc -v
复制代码

编写TS第一个程序

1.Hello World 环节

创建一个greet.ts文件,编写代码

// greet.ts文件
function greet(name) {
    return `hello ${name}`;
}

let user = 'mss';
console.log(greet(user));
复制代码

可以看出,TypeScript与JavaScript的基础语法几乎一样。

2.编译执行代码

  • 在编辑器终端中输入tsc greet.ts (tsc + 文件名称),运行 TypeScript 编译器,把ts文件编译成js文件。
  • 再在终端中输入node greet.js (node + 文件名称) 执行程序。
  • 可以点开查看TS文件下编译好的js文件。

TS类型注解

1.ts程序在传递参数时可以指定数据类型,还记得上文所说的 Self-documenting 吗?就是这个意思。

function greet(name: string) {
    return `hello ${name}`;
}

let user = 'mss';
console.log(greet(user));
复制代码

2.注意点

  • 传递参数的时候不允许多传或者少传。否则TypeScript会警告你代码可能不会按预期执行。
  • 指定了参数的类型的时候,如果传递其他类型参数,ts文件会提示报错,但是还是能编译出对应的js文件。
  • 开发中一定要注意报错警告,虽然js文件编译出来是没有问题的,可能会引起未知的错误。

TS中定义接口

1.在ts中使用 interface 定义接口,接口可以理解为一个对象。

接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

interface Person {
    firstName: string,
    lastName: string
}


function greet(name: Person) {
    return `hello ${name.firstName} and ${name.lastName}`;
}

let user = {
  firstName: 'mss',
  lastName: 'ghk'
};

console.log(greet(user));
复制代码

2.注意点

  • 定义了接口之后,传递参数就需要完全匹配
  • 在编译js文件的时候,会忽略掉我们定义的接口

TS中定义类

1.TypeScript 支持 JavaScript 的新特性,比如支持基于类的面向对象编程。

在类的声明上可以注明所有的成员变量,这样比较一目了然。

// 创建了一个 User 类,它带有一个构造函数和一些公共字段。
// 因为类的字段包含了接口所需要的字段,所以他们能很好的兼容。
class User {
    // 注明所有的成员变量
    fullName: string;
    firstName: string;
    lastName: string;
    constructor(firstName:string,lastName:string){
        this.firstName = firstName;
        this.lastName = lastName;
        this.fullName = `${firstName} ${lastName}`;
    }
}


//定义接口
interface Person {
    firstName: string
    lastName: string
}

function greet(name: Person) {
    return `hello ${name.firstName} and ${name.lastName}`;
}

let user = new User("mss","ghk");
console.log(greet(user));
复制代码

2.重新运行 tsc greet.ts,会看到 TypeScript 里的类只是一个语法糖,本质上还是 JavaScript 函数的实现。

TS基础类型

TypeScript 支持与 JavaScript 几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。

布尔值

最基本的数据类型就是简单的 true/false 值,在JavaScript 和 TypeScript 里叫做 boolean(其它语言中也一样)。

let bool: boolean = false;
复制代码

数字

和 JavaScript 一样,TypeScript 里的所有数字都是浮点数。 这些浮点数的类型是 number。 除了支持十进制和十六进制字面量,TypeScript 还支持 ECMAScript 2015中引入的二进制和八进制字面量。

let binaryLiteral: number = 0b10100;  //二进制的20
let octalLiteral: number = 0o24;  //八进制的20
let decLiteral: number = 20;  //十进制的20
let hexLiteral: number = 0x14;  //十六进制的20
复制代码

字符串

像其它语言里一样,我们使用 string 表示文本数据类型。 和 JavaScript 一样,可以使用双引号(")或单引号(')表示字符串。

let name: string = 'bob';
name = 'smith';
复制代码

数组

TypeScript 像 JavaScript 一样可以操作数组元素。 有两种方式可以定义数组。

  • 第一种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组:
let list: number[] = [1, 2, 3];
let list2:string[] = ["a","b","c"];
复制代码
  • 第二种方式是使用数组泛型,Array<元素类型>
let list: Array<number> = [1, 2, 3]
复制代码

元祖Tuple

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。

1.比如,你可以定义一对值分别为 stringnumber 类型的元组。

// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
复制代码

2.当访问一个已知索引的元素,会得到正确的类型,可以调用该类型的方法。

console.log(x[0].substr(1)) // OK
console.log(x[1].substr(1)) // Error, 'number' 不存在 'substr' 方法
复制代码

3.注意

TyeScript中文文档上面说:当访问一个越界的元素,会使用联合类型替代,实例代码来自官方文档

x[3] = 'world'; // OK, 字符串可以赋值给(string | number)类型

console.log(x[5].toString()); // OK, 'string''number' 都有 toString

x[6] = true; // Error, 布尔不是(string | number)类型
复制代码

但自从 TypeScript 3.1 版本之后,访问越界元素会报错,我们不应该再使用该特性。

如何验证?
可以自行下载安装3.1版本之前的TypeScript进行测试,3.1版本之后的TypeScript访问越界元素会报错。安装指令 npm Install -g typescript@3.0

枚举

1.enum 类型是对 JavaScript 标准数据类型的一个补充。使用枚举类型可以为一组数值赋予友好的名字。

enum Color {Red, Green, Blue}
let c: Color = Color.Green
复制代码

默认情况下,从 0 开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 1 开始编号:

enum Color {Red = 1, Green, Blue};
let c: Color = Color.Green
复制代码

或者,全部都采用手动赋值:

enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green
复制代码

2.枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为 2,但是不确定它映射到 Color 里的哪个名字,我们可以查找相应的名字:

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];

console.log(colorName);  // 显示'Green',因为上面代码里它的值是2
复制代码

3.被编译后的ts代码

var Color;
(function (Color) {
    Color[Color["red"] = 1] = "red";
    Color[Color["blue"] = 2] = "blue";
    Color[Color["green"] = 3] = "green";
})(Color || (Color = {}));
var colorName = Color[2];
console.log(colorName);
复制代码

Any

1.有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。

这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。

这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any 类型来标记这些变量:

let notSure: any = 4
notSure = 'maybe a string instead'
notSure = false // 也可以是个 boolean
复制代码

2.在对现有代码进行改写的时候,any 类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。

并且当你只知道一部分数据的类型时,any 类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:

let list: any[] = [1, true, 'free']
list[1] = 100 //可以修改
复制代码

Void

1.某种程度上来说,void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void

function warnUser(): void {
  console.log('This is my warning message') //什么也不输出
}
复制代码

2.声明一个 void 类型的变量没有什么大用,因为你只能为它赋予 undefinednull

let unusable: void = undefined;
复制代码

null 和 undefined

1.TypeScript 里,undefinednull 两者各自有自己的类型分别叫做 undefinednull。 和 void 相似,它们的本身的类型用处不是很大:

let u: undefined = undefined
let n: null = null
复制代码

2.默认情况下 nullundefined 是所有类型的子类型。 就是说你可以把 nullundefined 赋值给 number 类型的变量。

// index.ts文件
let num: number = 3;
num = null; //可以赋值
num = undefined; //可以赋值
复制代码

3.然而,当你在编译时指定了 --strictNullChecks 标记,nullundefined 只能赋值给 void 和它们本身,否则就会报错(如下图)。

let num: number = 3;
num = null; // 不可以把null赋值给num
复制代码

终端中输入tsc index.ts --strictNullChecks指定了标记,会报错

4.如果在某处想传入一个 stringnullundefined,可以使用联合类型 string | null | undefined

let num: number | null = 3;
num = null; 
复制代码

Never

1.never 类型表示的是那些永不存在的值的类型。 例如, never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never 类型,当它们被永不为真的类型保护所约束时。

2.never 类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是 never 的子类型或可以赋值给never 类型(除了 never 本身之外)。 即使 any 也不可以赋值给 never

3.下面是一些返回 never 类型的函数:

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
    return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
    }
}
复制代码

Object

1.object 表示非原始类型,也就是除 numberstringbooleansymbolnullundefined 之外的类型。

2.使用 object 类型,就可以更好的表示像 Object.create 这样的 API。例如:

// declare是一个关键字,表示申明一个东西
// 下面这句意思是,声明了一个create方法,
// 采用联合类型指定为参数为object或者null,返回值是一个void
declare function create(o: object | null): void

create({ prop: 0 }) // OK,因为参数是一个对象
create(null) // OK,因为参数是null

create(42) // Error
create('string') // Error
create(false) // Error
create(undefined) // Error
复制代码

类型断言

1.有时候你会遇到这样的情况,你会比 TypeScript 更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。

类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript 会假设你,程序员,已经进行了必须的检查。

2.类型断言有两种形式。

2.1 其一是“尖括号”语法:

// 首先定义一个any类型的值,此时编译器不知道这是什么类型
let someValue: any = 'this is a string'

// 此时你断言someValue是一个字符串,
// 使用了尖括号语法,强制将any转换为string类型,
// 此时someValue就是一个字符串类型了,并且具有length属性,
// 因此指定了strLength是number类型
let strLength: number = (<string>someValue).length
复制代码

2.2 另一个为 as 语法:

let someValue: any = 'this is a string'

let strLength: number = (someValue as string).length
复制代码

总结:这篇就是一个大量参考了官方文档的副本,不过比官方文档5分钟上手TypeScript的要详细一点。上文提到的定义接口、联合类型等复杂一点的东西,后面的笔记会详细说到。

个人觉得TypeScript这个名字起得非常形象,感觉ts的语言核心就是它的类型,在开发中不断的对数据进行限制和检测,避免了很多未知错误。虽然难写(没有,不是,假的),但是确实大大会降低bug率且易于维护。不仅仅是一个趋势,也是对个人编码的一种约束吧。