ts 一本通

js 缺什么

  • 类型检查
  • 语言扩展
  • vscode 补全

重塑类型思维。

强类型,弱类型语言。

java C# 等传统高级语言看 js

  • 面向对象,继承,多态,接口,命名空间,变量修饰,构造函数,访问器get set 静态属性
  • 委托
  • 泛型(随意的类型)
  • 反射(看包内的数组,动态分析东西是什么)
  • 集合 数据结构高级动态语言。
  • 动态数组ArrayList Hashtable SortedList Stack Queue 匿名方法
  • 拆箱
  • 多线程 worker

静态语言,动态语言: c++ 和 js

属性偏移量。js动态计算,c++在编译阶段确定属性偏移量。这里放一张图片。

动静语言内部细节对比

动态语言争议大:

  • 类型宽松。语言灵活性。
  • bug可能隐藏。单元测试发现
  • 运行性能差。v8区改善
  • 可读性差。用工具区生成。

试用

1
2
3
npm i -g typescript
touch tsconfig.json
touch index.ts

需要配置webpack ts-loader 等

ts 入门

目前ts 3.7

基本类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// number string boolean
const num: number = 5
const name1: string = "xinbao"

// Array
const list1: number[] = [1, 2, 3] // 常规
const list2: Array<number> = [1, 2, 3] // 数组泛型

const list3: Array<number | string> = [1, 2, 3, "4"]

// tuple 元组  是要求的数组,定义了内部的结构类型

let x: [string, number]

// object
let obj1: { id: number; y: number } = { id: 1, y: 2 }
// 试用联合类型,再 number | undefined

// any

// void

// Null Undefined
const u: undefined = undefined;
const n: null = null;

// never 永不存在的值类型。返回值是抛出异常,不会有返回值的函数表达式,箭头函数的返回值类型

// 类型断言 type assertions
// 明确知道结构
const sV: any = "q";
// let slen: number = (<string>sV).length;
let slen: number = (sV as stirng).length;

枚举

把一组数值 变为 有意义的名字。

用途:抽离常量,单独维护,节省记忆成本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
enum Color {
Red = 1,
Green = 2,
Blue = 4
}
let c: Color = Color.Red

const enum Month {
a = 2,
b = 4,
c
} // 编译之后不显示

let m = [Month.c] // 这部分代码编译之后是 let m = [5 /* c */];

// 也可以反查
let name:string = Color[2]
name // Green

接口 Interface

实现对对象的类型定义,不会编译,再开发阶段起辅助作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Data {
name: string;
age?: number; // 可选属性
[x: string]: any; // 字符串索引签名
readonly id: number; // 只读
}


function fu(persion: Data){
persion.name
}

interface StringArray {
[index: number]: string
} // 字符串数组
let chars: StringArray = ["a", "b"]

函数类型的接口

1
2
3
4
5
6
7
8
9
10
type Add = (x: number, y: number) => number
let add: Add = (a, b) => a + b

// 混合接口
interface Lib {
(): void
version: string
dost(): void
}
let lib: Lib = (() => { }) as Lib

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function add4(x: number, y?: number) {
return y ? x + y : x
}
add4(3, 3)
// 默认参数

function add5(x: number, y = 0, z: n) {}

// 剩余参数
function add6(x: number, ...rest: number[]) {
return x + rest.reduce((pre, cur) => pre + cur)
}
console.log(add6(1, 2, 3))

// 函数重载
function add8(...rest: number[]): number
function add8(...rest: string[]): string
function add8(...rest: any[]): any
{
let first = rest[0]
if (typeof first === "string") {
return rest.join("")
}
if (typeof first === "number") {
return
}
}

也包含了 继承 抽象 接口 setter getter等。

类里的属性方法默认是 public。也可以被设置成 private protected.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 类

class Dog {
constructor(name: string) {
this.name = name
}
name: string
run() {}
}

//log Dog.prototype 没有name
let dog = new Dog("w^w")
//log dog

class Husky extends Dog {
constructor(name: string, color: string) {
super(name)
this.color = color
}
color: string
}

// 修饰符 默认 Public
// private protected

class Husky2 extends Dog {
constructor(name: string, public color: string) {
super(name)
this.color = color
}

// 通过添加public 省略
// color: string
}

抽象类 多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 抽象类,只能被继承,而不能实例化的类
abstract class Animal {
// constructor() {}
eat() {}
}

// let a = new Animal() // 失败,

class Dog extends Animal {
name: string
constructor(name) {
super()
this.name = name
}
run() {}
}

var dog = new Dog("xx")
dog.eat()

类和接口的关系

包含关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 接口约束类的公有成员
// impl - element
// implelement

interface Human {
name: string
eat(): void
}

// 可以定义自己的属性
class A implements Human {
name: string
constructor(name: string) {
this.name = name
}
eat() {}
}

// 接口Man 拓展了另一个接口
interface Man extends Human {
run(): void
}

interface Child {
cry(): void
}

interface Boy extends Man, Child {}

// let boy: Boy = {}
// Type '{}' is missing the following properties from type
// 'Boy': run, name, eat, cry

class Auto {
state = 1
// private state2 = 0
}
interface AutoInterface extends Auto {}
class C implements AutoInterface {
state = 1
state2 = 1
}
class Bus extends Auto implements AutoInterface {}

泛型

实现一个打印函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 可以使用联合类型

function log_a(value: string | string[]): string | string[] {
return value
}

function log_b(value: any) {
return value
}
// 可以使用 any
// 但这样丢失了一些信息,不知道约束关系

// 传入T 返回T
function log<T>(value: T): T {
return value
}

log<string[]>(["1"])

// 类型推断,自动获取
log(["a"])

// 定义函数类型
type TypeLog = <T>(value: T) => T

// 泛型函数
let myLog: TypeLog = log

// 泛型接口,和类型别名等价
interface Log2 {
<T>(value: T): T
}

interface Log3<T> {
(value: T): T
}
let myLog2: Log3<number> = log

泛型类和泛型约束。确实有点难

  1. 函数和类可以借助泛型,支持多种类型,增强程序的扩展性
  2. 不必写多条函数重载,长长的联合类型,增强代码的可读性
  3. 灵活控制类型之间的约束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 泛型可以约束类的成员
// 泛型类与泛型约束
class Log<T> {
// 不能约束静态成员 静态成员不能引用类类型参数。
run(val: T) {
return val
}
}
let log1 = new Log<number>()
log1.run(1)

// 泛型约束
interface Length {
length: number
}
function log3<T extends Length>(val: T): T {
console.log(val, val.length)
return val
}
log3([1])

基本概念就都结束了。


类型推断

会自动进行推断,从右到左。

上下文类型推断。

1
2
3
4
5
6
7
8
9
// 类型断言
interface Foo {
bar: number
}
// let foo = {} as Foo
let foo: Foo = {
bar: 0
}
foo.bar

兼容性

类型保护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 类型保护
enum Type {
Strong,
Week
}

class Java {
helloJava() {}
}
class JavaScript {
helloJs() {}
}

// 类型保护函数
function isJava(lang: Java | JavaScript): lang is Java {
return (lang as Java).helloJava !== undefined
}

function getLanguage(type: Type) {
let lang = type === Type.Strong ? new Java() : new JavaScript()

// ugly code.
// if ((lang as Java).helloJava) {
// ;(lang as Java).helloJava()
// }
// 第一种方法 instanceof
// if (lang instanceof Java) {
// lang.helloJava()
// } else {
// lang.helloJs()
// }

// 第二种方法,给class添加 静态
// typeof 类型保护

// if(isJava()){}
}

// 类型断言
// 类型保护
// 能够在特定的区块中保证变量是某种特定的类型。
1
2
3
4
5
6
7
8
9
10
11
// 交叉类型 联合类型
interface DogInterface {
run(): void
}
interface CatInterface {
jump(): void
}
let pet: DogInterface & CatInterface = {
run() {},
jump() {}
}

脱离技术,进入工程化

模块系统

  • es6模块系统

可以导出值和接口。
可以导入。

  • node模块
1
2
3
exports

require()
1
2
npm i -g ts-node
ts-node a.ts

可以通过配置修改编译模式。

编写类库

jquery是amd类库。jquery不支持ts,需要声明文件。

1
npm i @types/jquery -D

DefinitelyTyped 是最大的公共存储库,包括1000多个库文件。也有一个用来管理 TypeScript 定义的 Node.js 流行模块,叫 Typings。

如何写声明文件。

*.d.ts

1
declare function globalLib(options:flobalLb.Options):viod

new in 3.7

optional chaining 可选链

1
2
3
4
5
6
7
8
let x = foo?.bar.baz()

let x = (foo === null || foo === undefined)?undefined: foo.bar.baz()

// before
if(foo&&foo.bar&&foo.bar.baz){}
// after
if(foo?.bar?.baz){}

nullish coalescing
??

1
2
3
4
5
6
7
// after
let x = foo ?? bar()

// before
let x = (foo!==null&&foo!==undefined)? foo: bar()

// 也可以替代使用 ||
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// 函数部分

// 函数重载
function info(a:{name: string}): string
function info(a: string):
{name:string}
function info(){}

// void 定义返回类型 在函数里定义返回类型,就是必须定义返回
function sayMyName(name: string): string {
return name;
}
console.log(sayMyName("gogo"));



// 实现和继承
// implements extends

class dev1 implements IDev {
name = "x";
age = 20; // 可把这行注释掉
}


// extends 是继承父类


// 声明文件和命名空间 declare namespace
// declare 使用第三库需要引入它的声明文件,才能获得代码补全,接口提示等功能


// declare var a
// declare function


// 命名空间
// namespace x{}


// 也可以为其他js库定义 .d.ts 声明文件


declare namespace D3 {
export interface Sele {
Base: string;
}
}
declare var d3: D3.Sele;


// 访问修饰符
// private public protected
// 默认 public
// private 不能再类之外访问
class Anim {
private name: string;
constructor(theName: string) {
this.name = theName;
}
}
// let a = new Anim('cat').name // 私有的无法访问


// protected 和 private 类似。protected 成员可以在派生类中访问
// 从父类继承的自雷里,可以访问 protected


// 可选参数 ?: 非空断言操作费 !.
function aa1(firstName: string, lastName?: string) {}
// 这里传参第二个参数可以省略


// let s = e!.name // 不为空继续访问name属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 泛型 generics
// 定义函数 接口 类 的时候,不预先指定具体的类型,使用的时候再执行类型,增加代码通用性

// 不适用泛型
interface R{
ok: 0 | 1,
data: Future[]
}
//使用泛型
interface Result<T>{
ok: 0|1;
data: T;
}
function g<T>():Result<T>{
const data:any={}
return {data}
}

function gen_fun1<T>(arg: T) {
return arg;
}
// gen_fun1<stirng>("go");


// 带any参数的方法
// 传入参数不一定有length,会报错
function any_func(arg: any): any {
console.log(arg.length);
return arg;
}
// array 泛型
// 参数类型是 array的泛型类型,肯定有length
function any_func2<T>(arg: Array<T>): Array<T> {
console.log(arg.length);
return arg;
}

请我喝杯咖啡吧~