目录
一.安装 TypeScript
- Typescript 官网地址: https://www.typescriptlang.org/zh/
- 安装命令:npm install -g typescript
- 检查基本配置:
- 编译 ts 文件:tsc test.ts
二.原始数据类型 和 Any
- 原始数据类型(primitive values):除了 Object 以外,所有类型都是不可变的(值本身无法被改变)
- Boolean、Number、String、Null、Undefined、BigInt、Symbol
- 注意的点:
- Number:可以写成二进制、八进制数字
- String:ts 中可以使用 es6 新增的 `模版字符串`
- undefined 和 null,是所有类型的子类型,也就是说 undefined 类型的变量,可以赋值给 number 类型的变量
let isDone: boolean = false // es6 还支持2进制和8进制 let age: number = 10 let binaryNumber: number = 0b1111 // es6 新增的 `模版字符串` 也可以用 let firstName: string = 'TeaMeow' let message: string = `Hello, ${firstName}, age is ${age}` // undefined 和 null,是所有类型的子类型 // 也就是说 undefined 类型的变量,可以赋值给 number 类型的变量 let u: undefined = undefined let n: null = null let num: number = undefined
let notSure: any = 4 notSure = 'maybe it is a string' notSure = 'boolean' // 在任意值上访问任何属性都是允许的: notSure.myName // 也允许调用任何方法: notSure.getName()
三.Array 和 Tuple(元组)
- 数组:let arrOfNumbers: number[] = [1, 2, 3, 4]
- 元组:let user: [string, number] = ['TeaMeow', 21]
// 最简单的方法是:「类型 + 方括号」来表示数组 let arrOfNumbers: number[] = [1, 2, 3, 4] // 数组的项中不允许出现其他类型: // 数组的方法的参数也会根据 数组在定义时 约定的类型进行限制: arrOfNumbers.push(3) arrOfNumbers.push('abc') // 元祖的表示和数组非常类似,只不过它将类型写在了里面 这就对每一项起到了限定的作用 let user: [string, number] = ['TeaMeow', 21] // 但是写少一项 就会报错 写多一项也会有问题 user = ['molly', 20, true]
四.interface 接口
- Duck Typing 概念:如果某个东西长得像鸭子,像鸭子一样游泳,像鸭子一样嘎嘎叫,那它就可以被看成是一只鸭子
- 接口其实就是非原始数据类型(比如 Object)的定义模板、规范
- 接口中可以定义 可选属性?,表示变量如果使用该接口,这个属性写不写都行
- 接口中可以定义 只读属性readonly,表示变量如果使用该接口,这个只读属性就只能被第一次初始化进行赋值,之后不能被赋值
- 只读属性 readonly 和 const 的区别联系:
- 区别:都表示除了第一次进行赋值外,无法进行二次更改
- 联系:readonly 用于接口中的某个属性,最后接口产生变量;const 用于常量
// 我们定义了一个接口 Person interface Person { name: string; age: number; } // 接着定义了一个变量 TeaMeow,它的类型是 Person。这样,我们就约束了 TeaMeow 的形状必须和接口 Person 一致 let TeaMeow: Person ={ name: 'TeaMeow', age: 20 } // 有时希望不要完全匹配一个形状,那么可以用可选属性: interface Person { name: string; age?: number; } let TeaMeow: Person = { name: 'Viking' } // 接下来还有只读属性,有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性 interface Person { readonly id: number; name: string; age?: number; }
五.函数
// 约定输入,约定输出 function add(x: number, y: number): number { return x + y } // 可选参数 function add(x: number, y: number, z?: number): number { if (typeof z === 'number') { return x + y + z } else { return x + y } } // 函数本身的类型 const add2: (x: number, y: number, z?:number) => number = add // interface 描述函数类型 interface ISum { (x: number, y: number): number } const sum2: ISum = sum
六.类型推论,联合类型 和 类型断言
1.联合类型
- 一个变量,既可能是数字,又可能是字符串的时候,就用到了 联合类型:let numberOrString: number | string
- 当 TypeScript 不确定一个联合类型的变量到底是哪个类型时,就只能访问此联合类型中 所有类型的 共有属性或方法
- 比如说某个联合类型的变量是 数字+字符串,那么,我们调用该变量的属性就只能调用 length、toString() 这种,数字和字符串都有的属性或方法
- numberOrString.length
- numberOrString.toString()
// 用中竖线分割两个类型 let numberOrString: number | string // 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,就只能访问此联合类型的所有类型里共有的属性或方法: numberOrString.length numberOrString.toString()
2.类型断言
// 可以用 as 关键字进行 类型断言,告诉 typescript 编译器: // 你没法判断我的代码,但是我本人很清楚,这里我就把它看作是一个 string,你可以给他用 string 的方法 function getLength(input: string | number): number { // 把 input 变量看作 字符串 const str = input as string if (str.length) { return str.length // 把 input 变量看作 数字 } else { const number = input as number return number.toString().length } }
3.类型守卫
- typescript 在不同的条件分支里面,智能缩小了范围,这样代码出错的几率就大大降低了
// typescript 在不同的条件分支里面,智能缩小了范围,这样代码出错的几率就大大降低了 function getLength2(input: string | number): number { if (typeof input === 'string') { return input.length } else { return input.toString().length } }
七.Class 类
- 面向对象的三大特点:
- 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口;调用者不需要(也不可能)知道细节,就能通过对外暴露的接口来访问该对象
- 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有自己的特性
- 多态(polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应,比如:动物叫 → 猫叫、狗叫,对于 叫 这个方法,不同动物类有不同的响应
- 类成员的访问修饰符:
- public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
- private 修饰的属性或方法是私有的,不能在声明它的类的外部访问,包括它的子类也不能访问
- protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中是允许被访问的
class Animal { name: string; constructor(name: string) { this.name = name } run() { return `${this.name} is running` } } const snake = new Animal('lily') // 继承的特性 class Dog extends Animal { bark() { return `${this.name} is barking` } } const xiaobao = new Dog('xiaobao') console.log(xiaobao.run()) console.log(xiaobao.bark()) // 这里我们重写构造函数,注意在子类的构造函数中,必须使用 super 调用父类的方法,否则会报错。 class Cat extends Animal { constructor(name) { super(name) console.log(this.name) } run() { // 在子类的构造函数中,必须使用 super 调用父类的方法,否则会报错 return 'Meow, ' + super.run() } } const maomao = new Cat('maomao') console.log(maomao.run())
八.类与接口
- 类实现某接口:implements => class Cellphone implements Radio
- 类实现多个接口:中间需要用 , 隔开 => 实现多个接口,需要中间用 逗号 隔开
interface Radio { switchRadio(trigger: boolean): void; } interface Battery { checkBatteryStatus(): void; } // 类实现(继承)某接口 implements class Car implements Radio { switchRadio(trigger) { return 123 } } class Cellphone implements Radio { switchRadio() { } } // 实现多个接口,需要中间用 逗号 隔开 class Cellphone implements Radio, Battery { switchRadio() { } checkBatteryStatus() { } }
九.枚举 Enums
- 枚举成员会被赋值为:从 0 开始递增的数字:console.log(Direction.Up) // 0
- 枚举做了反向映射,可以选出枚举中的第几项是什么:console.log(Direction[0]) // Up
// 定义枚举方向,这里的枚举成员会被赋值为:从 0 开始递增的数字 enum Direction { Up, // 被赋值为 0 Down, // 被赋值为 1 Left, // 被赋值为 2 Right, // 被赋值为 3 } console.log(Direction.Up) // 0 // 枚举做了反向映射,可以选出枚举中的第几项是什么 console.log(Direction[0]) // Up // 字符串枚举 enum Direction { Up = 'UP', Down = 'DOWN', Left = 'LEFT', Right = 'RIGHT', } const value = 'UP' if (value === Direction.Up) { console.log('go up!') }
十.泛型 Generics
1.泛型定义
- 泛型(Generics):在定义函数、接口或类的时候,不指定具体类型,使用时再指定类型
- 传入某个类型的参数,返回的也是这个类型
- 泛型也可以传入多个值,比如第一个参数传入字符串类型,第二个参数传入数字类型,返回的时候,让第二个参数先返回来,也就是返回数字,第一个参数后返回来,也就是返回字符串
function echo(arg) { return arg } const result = echo(123) // 上面传入了数字,但是返回了 any // 下面规定,传入 T类型参数,也需要返回 T类型结果 function echo<T>(arg: T): T { return arg } const result = echo(123) // 泛型也可以传入多个值 // 下面规定,传入 T,U 类型参数,返回 U,T 类型参数 function swap<T, U>(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]] } // 传入字符串、数字类型的参数,返回数字、字符串类型的参数(比原来反了) const result = swap(['string', 123])
2.泛型约束
- 在函数内使用泛型时,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法,比如不确定传入的参数是否有 length,就不能操作 length 属性
- 泛型继承约束:T extends IWithLength
- 继承那个接口表示,接下来传入的参数中必须包含 length 这个属性
function echoWithArr<T>(arg: T): T { console.log(arg.length) return arg } // 上例中,泛型 T 不一定包含属性 length,我们可以给他传入任意类型,当然有些不包括 length 属性,那样就会报错 interface IWithLength { length: number; } // 泛型继承约束,继承那个接口表示,接下来传入的参数中必须包含 length 这个属性 function echoWithLength<T extends IWithLength>(arg: T): T { console.log(arg.length) return arg } echoWithLength('str') // 传入字符串包含了 length属性 const result3 = echoWithLength({length: 10}) // 传入的对象包含了 length属性 const result4 = echoWithLength([1, 2, 3]) // 传入的数组包含了 length属性
3.泛型、类、接口
- 可以给类和接口加泛型,进行约束
class Queue { private data = []; push(item) { return this.data.push(item) } pop() { return this.data.shift() } } const queue = new Queue() queue.push(1) queue.push('str') console.log(queue.pop().toFixed()) console.log(queue.pop().toFixed()) //在上述代码中存在一个问题,它允许你向队列中添加任何类型的数据,当然,当数据被弹出队列时,也可以是任意类型。在上面的示例中,看起来人们可以向队列中添加string 类型的数据,但是在使用的过程中,就会出现我们无法捕捉到的错误,比如,传入字符串,无法进行取整 class Queue<T> { private data = []; push(item: T) { return this.data.push(item) } pop(): T { return this.data.shift() } } const queue = new Queue<number>() // 泛型和 interface interface KeyPair<T, U> { key: T; value: U; } let kp1: KeyPair<number, string> = { key: 1, value: "str"} let kp2: KeyPair<string, number> = { key: "str", value: 123}
十一.类型别名、交叉类型
1.类型别名
// 下面的函数类型是:(x: number, y: number) => number let sum: (x: number, y: number) => number const result = sum(1,2) // 将函数类型取个类型别名 type PlusType = (x: number, y: number) => number let sum2: PlusType // 支持联合类型 type StrOrNumber = string | number let result2: StrOrNumber = '123' result2 = 123 // 字符串字面量 type Directions = 'Up' | 'Down' | 'Left' | 'Right' let toWhere: Directions = 'Up'
2.交叉类型
- &:表示 和,比如下面:
interface IName { name: string } // IPerson 既要包含 IName,又要包含 {} 中的内容 type IPerson = IName & { age: number } let person: IPerson = { name: 'hello', age: 12}
十二.声明文件
十三.内置类型
const a: Array<number> = [1,2,3] // 不同的文件中有多处定义,但是它们都是 内部定义的一部分,然后根据不同的版本或者功能合并在了一起,一个interface 或者 类多次定义会合并在一起。这些文件一般都是以 lib 开头,以 d.ts 结尾,告诉大家,我是一个内置对象类型欧 const date: Date = new Date() const reg = /abc/ // 还可以使用 build in object,内置对象 // 比如 Math 与其他全局对象不同,Math 不是一个构造器。Math 的所有属性与方法都是静态的 Math.pow(2,2) // DOM 和 BOM 标准对象 // document 对象,返回的是一个 HTMLElement let body: HTMLElement = document.body // document 上面的query 方法,返回的是一个 nodeList 类型 let allLis = document.querySelectorAll('li') // 添加事件,document 上面有 addEventListener 方法,注意这个回调函数,因为类型推断,这里面的 e 事件对象也自动获得了类型,这里是个 mouseEvent 类型,因为点击是一个鼠标事件,现在我们可以方便的使用 e 上面的方法和属性。 document.addEventListener('click', (e) => { e.preventDefault() })
interface IPerson { name: string age: number } // 下面表示 name、age 都得传入 let TeaMeow: IPerson = { name: 'viking', age: 20 } // partial,它可以把传入的类型都变成可选 // 改造接口,将他用 partial 包裹,使得所有的参数都变成可选的 type IPartial = Partial<IPerson> // 使用改造后的接口,这样 name、age 就变成可选参数了 let TeaMeow2: IPartial = { } // Omit,它返回的类型可以忽略传入类型的某个属性 // 改造接口,将他用 Omit 处理,使得 name 属性被忽略(可以不被传入) type IOmit = Omit<IPerson, 'name'> // 使用改造后的接口,这样 name 就可以被忽略了 let TeaMeow3: IOmit = { age: 20 }
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。