TpyeScript 语言
强类型 与 弱类型 (类型安全)
-
强类型:强类型语言中不允许有任何的隐式类型转换
- 优势:
- 错误更早暴露
- 代码更智能,编码更准确(智能提示
- 重构代码更牢靠
- 减少不必要的类型判断
- 优势:
-
弱类型:弱类型语言中可以允许任何的隐式类型转换
-
问题:
// 需要等待代码具体执行时才能够发现错误 const obj = {}; setTimeout(()=>{ obj.foo() }) // 造成函数功能有可能发生改变 function sum (a , b){ return a + b } sum(100, 100) sum(100, '100') // 造成对对象索引器错误的用法 const obj = {}; obj[true] = 100; console.log(obj['true'])变量类型允许随时改变的特点,不是强弱类型的差异
-
动态类型 与 静态类型 (类型检查)
- 动态类型:运行阶段才能明确变量的类型,且变量类型可以更改
- 静态类型:变量声明式该变量的类型就是明确的,且不可更改的
TS中的原始数据类型
const a: string = 'a'
const b: number = 1
const c: boolean = true
const d: void = undefined
const e: null = null
const f: undefined = undefined
const g: symbol = Symbol()
TS中的Object类型
TS中的Object类型,并不单指普通的对象,泛指所有的非原始数据类型(对象,数组,函数)
const foo: object = function(){} // [] // {}
TS中的数组类型
// 两种不同的写法
const arr1: Array<number> = [1,2,3]
const arr2: number[] = [1,2,3]
TS中的元组类型(Tuple Types)
元组:明确元素数量,以及每个元素类型的数组
const tuple: [number, string] = [1, 'foo']
TS中的枚举类型
// 枚举内的变量可以默认不赋值,在不赋值值得情况下,默认从0开始依次递增,若第一个值默认赋值为一个非零数字,则从这个数值开始依次递增
// 枚举内的变量若赋值为字符,则不会发生依次递增,且需要明确每一个变量的值
// 默认枚举方式会入侵到运行时的代码,即会影响编译后的代码,枚举对象会编译为一个 双向键值对函数
// 若要避免入侵,可以使用常量枚举,在 enum 前加 变量声明关键字即可(const)
enum PostStatus {
Draft = 0,
UnPublished = 1,
Published = 2
}
const post = {
title: 'Hello TypeScript',
content:'TypeScript is a type superset of JavaScript',
status: PostStatus.Draft
}
TS中的函数类型
// 声明式函数
function func1(a: number, b: string): string {
return 'func1'
}
// 可选参数写法
function func1(a: number, b: string, c?: string): string {
return 'func1'
}
// 任意个数的参数
function func1(...rest: number[]): string {
return 'func1'
}
// 函数表达式的方式
const func2: (a: number, b: string) => string = a: number, b: string): string {
return 'func2'
}
TS中的任意类型
// TS中不会对any进行类型检查,可能导致出错,所以应该尽量避免使用any类型
function stringify (value: any){
return JSON.stringify(value)
}
TS的隐式类型推断(Type Inference)
// 此处TS会推断age类型为number
let age = 18;
TS的类型断言(Type Assertion)
const nums = [100, 200, 300, 400]
// 此处TS会推断res的类型为 number | undefined
const res = nums.find(i => i > 0)
// 因为我们知道res一定为number类型,所以需要告诉TS,res一定为number类型。有以下两种方法
const num1 = res as number
const num2 = <number>res // 此处在使用JSX时,会产生混淆,故不推荐使用此方法
TS的接口(Interfaces)
接口是用来约定一个对象的结构。
// 定义一个接口
interface Post {
title: string;
content: string;
subTitle?: string; // 表示可选成员
readonly summary: string // readonly 表示只读成员
}
// 接口的使用
function printpost(post: Post){
console.log(post.title)
console.log(post.content)
}
const hello: Post = {
title:'hello',
contentL: 'This is a hello',
summary: 'Title Summary'
}
// 接口可以理解为自己自定义一个类型,这个类型中规定了需要具有哪些元素,以及具体元素的类型
// 动态成员 定义的时候无法知道有哪些具体的成员
interface Cache {
[key:string]: string
}
const cache:Cache = {
a: 'a'
}
TS类的基本使用
TS中增强了类的使用方法
- ts中必须先声明类的属性,若属性没有默认值,必须要构造函数中赋值
class Person {
// ts中必须先声明类的属性,若属性没有默认值,必须要构造函数中赋值
name: string,
age: number,
construction(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHi(msg: string) : string {
return 'hi' + msg;
}
}
-
类的访问修饰符
public默认值,公开属性private私有属性,只能在类的内部使用,外部无法访问protected类似与private; 相较于private来说,使用protected的属性,是可以被继承的类访问的
class Person { // ts中必须先声明类的属性,若属性没有默认值,必须要构造函数中赋值 public name: string, private age: number, protected gender: boolean, construction(name: string, age: number) { this.name = name; this.age = age; } sayHi(msg: string) : string { return 'hi' + msg; } class Student extends Person { // constructor 也是可以使用修饰符修饰的 // 此处使用private,意味着无法在外部无法使用new关键字创建Student类的属性,但是可以通过在类中实现一个静态方法创建实例 private constructor (name: string, age: number) { super(name, age); // gender是 protected 的属性,可以在继承 Person 的类中访问 console.log(this.gender); } static create(name: string, age: number){ return new Student(name, age) } } -
TS中的只读属性
readonlyclass Person { // ts中必须先声明类的属性,若属性没有默认值,必须要构造函数中赋值 public name: string, private age: number, protected readonly gender: boolean, // readonly 表示只能读取,不可重新赋值 construction(name: string, age: number) { this.name = name; this.age = age; } sayHi(msg: string) : string { return 'hi' + msg; } } -
类与接口的比较
- 不同的类之间可能会存在共同的特征,我们把这些共同的特征抽象出来,叫做接口
interface Eat { eat (food: string) : void } interface Run { run (disrance: number) : void } // 建议一个接口只定义一种方法(能力) /* interface EatAndRun { eat (food: string) : void run (disrance: number) : void } */ class Person implement Eat, Run { eat (food: string): void { console.log('优雅的吃:' + food) } run (distance: number): void { console.log('直立行走' + distance) } } class Animal implement Eat, Run{ eat (food: string): void { console.log('没有形象的吃:' + food) } run (distance: number): void { console.log('爬行' + distance) } } -
抽象类 抽象类和接口类似,也是用来约束子类当中必须要有某些成员,相较于接口来说,抽象类还必须包含具体的实现,而接口只是约束不包括实现。一般较大的类,建议使用抽象类
abstract class Animal implement Eat, Run{ eat (food: string): void { console.log('没有形象的吃:' + food) } abstract run (distance: number): void } // 抽象类不能使用new关键字创建实例,只能通过继承实现子类 class Dog extends Animal { run (distance: number) { console.log('四角爬行' + distance) } } const d = new Dog() d.eat('骨头') d.run(20) -
泛型(Generics) 泛型就是在定义类、函数、或者接口的时候,暂时不定义具体的类型,等到具体使用的时候再确认具体的类型的一种特征。意义在于极大程度的复用代码
// 此处的T就是将来使用时传递进入的具体类型。 function createArray<T>(length: number, value: T): T { return Array<T>(length).fill(value); } const stringArray = createdArray<string>(3, 'aaa') -
类型声明
- 在使用第三方插件库的时候,可能会碰到未定义类型的方法(看不到类型提示),这个时候可以使用类型声明
import { camelCase } from 'lodash'; declare function camelCase (input: string): string // 此处声明之后就可以直接使用 const res = camelCase('hello world') // 目前很多插件都有类型注声明文件,到时候可以去安装一下。另外,很多插件自身就包含了类型声明文件,无需单独再去安装或者声明