【TypeScript】速通篇
目录
0 前言
1 准备工作
1.1 安装typescript包
1.2 简化运行TS
2 类型注解
2.1 常用类型
2.1.1 原始类型
2.1.2 数组类型
2.1.3 联合类型
2.1.3.1 类型别名
2.1.4 函数类型
2.1.4.1 void类型
2.1.4.2 可选参数
2.1.5 对象类型
2.1.5.1 可选属性
2.1.5.2 接口
2.1.5.2.1 接口与类型别名的区别
2.1.5.2.2 接口继承
2.1.6 元组
2.1.7 类型推论
2.1.8 类型断言
2.2 其他类型
2.2.1 字面量类型
2.2.2 枚举类型
2.2.3 any类型
2.2.4 typeof运算符
2.3 高级类型
2.3.1 class类
2.3.1.1 构造函数
2.3.1.2 实例方法
2.3.1.3 class继承
2.3.1.4 可见修饰符
2.3.1.5 只读修饰符
2.3.2 类型兼容性
2.3.2.1 class
2.3.2.2 接口
2.3.2.3 函数
2.3.3 交叉类型
2.3.3.1 交叉类型(&)和接口继承对比(extends)
2.3.4 泛型
2.3.4.1 泛型约束
2.3.4.2 多泛型变量
2.3.4.3 泛型接口
2.3.4.4 泛型类
2.3.4.5 泛型工具
2.3.4.5.1 Partial
2.3.4.5.2 Readonly
2.3.4.5.3 Pick
2.3.4.5.4 Record
2.3.5 索引签名类型
2.3.6 映射类型
2.3.6.1 索引查询
3 类型声明文件
3.1 介绍
3.2 已有的类型声明文件
3.3 编写类型声明文件
0 前言
黑马程序员视频地址:TypeScript零基础入门到实战全套教程
1 准备工作
1.1 安装typescript包
npm i -g typescript
检测版本:
tsc -v
ts文件需要先转化成js文件再运行:
第一步:
tsc xxx.ts
注意:使用该命令会在同一目录下生成一个同名的js文件
第二步:
node xxx.js
此过程过于繁琐,因此需要 1.2 中的包
1.2 简化运行TS
安装包:
npm i -g ts-node
使用命令:
ts-node xxx.ts
注意:本质上也是先转化再运行,但是此命令并不会生成同名的js文件
注意:若安装了上述两个包,仍然无法运行ts文件,可以尝试运行以下命令之后再试
npx tsc --init
2 类型注解
为变量声明类型
如:
let count: number = 10
此后若修改该变量的类型,则会实时报错
2.1 常用类型
2.1.1 原始类型
const a: number = 1
const b: string = '1'
const c: boolean = true
const d: undefined = undefined
const e: null = null
const f: symbol = Symbol()
2.1.2 数组类型
写法一:推荐
const a: number[] = [1, 2, 3]
const b: string[] = ['1', '2', '3']
const c: boolean[] = [true, false]
const d: null[] = [null, null]
// ...
写法二:不推荐
const a: Array<number> = [1, 2, 3]
const b: Array<string> = ['1', '2', '3']
const c: Array<boolean> = [true, false]
const d: Array<null> = [null, null]
// ...
2.1.3 联合类型
符号:|
示例:
const a: (number | string)[] = [1, 2, '3', '4'] // 既含有数字又含有字符串的数组
const b: number | string[] = 1 //值为数字或字符串数组
const c: number | string[] = ['1', '2'] //值为数字或字符串数组
2.1.3.1 类型别名
当一种复杂类型被多次使用,为了简化书写量,可以给这种类型起一个别名
示例:
type customArr = (number | string)[]
const a: customArr = [1, 2, '3', '4']
const b: customArr = [5, 6, '7', '8']
2.1.4 函数类型
第一种:单独指定参数与返回值的类型
// 写法一:函数声明
function add(a: number, b: number): number{return a + b
}add(1, 2)// 写法二:函数表达式(箭头函数)
const subtract = (a: number, b: number): number => {return a - b
}subtract(1, 2)
第二种:同时指定参数与返回值类型(只适用于函数表达式)
const add: (a: number, b: number) => number = (a, b) => {return a + b
}add(1, 2)
理解:在声明add这个变量时,同时给他以箭头函数的形式设置参数类型,这样再在后面使用函数表达式时就不需要再次写类型了
第一步:
const add = (a, b) => {return a + b
}
第二步:
add: (a: number, b: number) => number
2.1.4.1 void类型
写法一样,函数没有返回值时就写void
此处不再赘述
2.1.4.2 可选参数
符号: ?
注意:可选参数必须在必选参数后面
示例:
function add (a: number, b?: number): void {console.log(a, b)
}
2.1.5 对象类型
示例:先给对象结构声明属性的类型,再赋值
const object: { name: string, age: number, sayHi(a: number): void } = {name: 'USERA_001',age: 19,sayHi(a){console.log(a)}
}
注意:函数用法与上面相同,在给属性中函数声明类型的时候要加小括号
对象中的方法也可以用另一种方法写,即统一给函数名声明参数即返回值的类型
const object: { name: string, age: number, sayHi: (a: string) => void } = {name: 'USERA_001',age: 19,sayHi(a){console.log(a)}
}
声明属性类型时,如果写在一行,不同属性值之间要用分号或逗号分割,也可以选择分行写:
const object: {name: stringage: numbersayHi(a: number): void
} = {name: 'USERA_001',age: 19,sayHi(a){console.log(a)}
}
2.1.5.1 可选属性
符号: ?
用法与函数类型相同,此处不再赘述
2.1.5.2 接口
与联合类型中的类型别名相同,同样是为了复用
示例:
interface information {name: stringage: numbersayHi(a: number): void
}const object: information = {name: 'USERA_001',age: 19,sayHi(a){console.log(a)}
}
2.1.5.2.1 接口与类型别名的区别
接口只能给对象指定类型,而类型别名可以给任意类型使用
如,给对象使用类型别名:
type information = {name: stringage: numbersayHi(a: number): void
}const object: information = {name: 'USERA_001',age: 19,sayHi(a){console.log(a)}
}
2.1.5.2.2 接口继承
关键字:extends
示例:
interface Point1 {a: number,b: number
}interface Point2 extends Point1 {c: number
}const object: Point2 = {a: 1,b: 2,c: 3
}
2.1.6 元组
特点:是数组的一种,规定了数组的个数与具体类型
示例:
const arr: [number, string] = [1, '2']
2.1.7 类型推论
可省略类型注解的场景:
1.声明变量并初始化时
2.决定函数返回值时
推荐:能省则省
使用le声明变量不指定类型时,由于类型推论机制,会自动根据值来指定类型
使用const声明常量不指定类型时,该常量的数据类型就是所被赋予的值
2.1.8 类型断言
获取元素赋值给变量时,变量的类型会显示的是最宽泛的类型,导致拿不到特定标签的特定属性,如a标签的href属性,因此需要类型断言,给其设置一个更细致的类型
// 写法一:推荐
const ele = document.querySelector('.ele') as HTMLAnchorElement// 写法二:了解,在react中会有冲突,不推荐
const ele = <HTMLAnchorElement>document.querySelector('.ele')
可以通过在浏览器中选中标签,然后使用console.dir打印$0对象,即可查看当前标签具体类型
2.2 其他类型
2.2.1 字面量类型
任意一个固定的值都可以作为一种类型,这会使得该变量的值只能是指定的这个值
字面量类型常配合联合类型使用,从而达到限制选择类型的效果
示例:
function a(action: 'up' | 'down' | 'left' | 'right') {console.log(action)
}a('up')
2.2.2 枚举类型
具有一组具名的常量,类似于上面代码中字面量与联合类型配合实现的效果
enum choose {up,down,left,right
}function a(action: choose) {console.log(action)
}a(choose.up)
注意:使用时需要用 枚举类型.具体常量 的形式
数字枚举:
这一组具名常量会以第一个常量的值为开始自增,默认第一个常量为0
如果为其第一个常量设置一个数字,如10,则后续常量的值会变成11、12...
字符串枚举:
若其中有一个常量被赋值一个字符串,则所有常量都必须赋予一个指定的值,因为字符串枚举没有自增长行为
注意:推荐使用字面量+联合类型的方法,因为枚举类型在转成js代码时,会被编译为js代码,而前者则会被自动忽略
2.2.3 any类型
不推荐使用!!!
使用该类型意味着可以对声明的变量进行任何操作,包括以函数形式调用,都不会报错!
这就失去了ts存在的意义
默认变成any类型的情况:
1.声明变量时,不给类型,不给值
2.函数形参不给类型
2.2.4 typeof运算符
功能一:查询变量或属性类型
let a: {b: number,c: string
} = {b: 1,c: '1'
}let d:number = 1console.log(typeof a)
console.log(typeof a.b)
console.log(typeof d)
功能二:引用变量或属性的类型
let o: {b: number,c: string
} = {b: 1,c: '1'
}let p: typeof o = {b: 2,c: '2'
}let h: typeof o.b = 1
与类型别名十分相似,但是 typeof 可以直接把已经有的类型拿过来使用
2.3 高级类型
2.3.1 class类
注意:黑马程序员的js系列课程中并没有讲class类,但ts课程中的内容与其大差不差
2.3.1.1 构造函数
js中,类的使用示例:
class Person{constructor(age, name){ // 设置动态属性this.age = agethis.name = name}hobby = 'football' // 静态属性
}const xm = new Person(18, 'zs')
console.log(xm)
注意:直接在构造函数内赋值即可,构造函数外无需再次声明
ts中则需额外声明:
class Person {constructor(name: string, age: number){this.name = namethis.age = age}name: stringage: numberhobby = 'football'
}const person = new Person('张三', 18)
console.log(person)
注意:不能给构造函数constructor指定返回类型,其他函数方法可以设置返回类型
2.3.1.2 实例方法
补课:js类中声明方法时,直接写方法名(),不能写function
注意:方法其实是定义在原型上的
在ts类中,声明方法的类型注解与函数相同
示例:
class Person {sayHi(content: string){console.log(content)}
}const zs = new Person()
zs.sayHi('你好')
2.3.1.3 class继承
方法一:extends(继承父类)
注意:此方法在js中也有
继承之后,即具有父类所有的属性与方法
示例:
class Father {move(){console.log('1')}
}class Son extends Father {walk(){console.log('2')}name = 'ls'
}const ls = new Son()
ls.move()
ls.walk()
console.log(ls.name)
方法二:implements(实现接口)
此种方法其实并不算继承类,只能算约束类
即必须写接口中提供的属性与方法
interface person {name: stringmove(): void
}class Baby implements person {name = '张三'move(): void {}
}
2.3.1.4 可见修饰符
第一种:public(默认):对该类及其实例化对象、子类及其实例化对象都可见
class Father {public move() {}
}const zs = new Father()
zs.move()class Son extends Father {}const ls = new Son()
ls.move()
第二种:protected:仅对该类及子类开放,实例化对象都不可见
class Father {protected move() {}
}const zs = new Father()
// zs.move() 这是错的class Son extends Father {walk(){this.move()}
}const ls = new Son()
// ls.move() 这是错的
第三章:private:仅对该类内部开放,该类的实例化对象以及子类及其实例化对象都不可见
class Father {private move() {}
}const zs = new Father()
// zs.move() 这是错的class Son extends Father {walk(){// this.move() 这是错的}
}const ls = new Son()
// ls.move() 这是错的
2.3.1.5 只读修饰符
注意:
1.只能给属性使用,不能给方法使用
2.如果不加类型注解,则会被识别为字面量类型
3.接口或{}表示的对象类型,也可以使用
示例:
class Person {readonly name: string = '张三'constructor(name: string){this.name = name}changeName(name: string){// this.name = name 这是错误的}
}const zs = new Person('李四')
// zs.name = '张三' 这是错误的
2.3.2 类型兼容性
ts采用的是结构化类型系统,即关注结构是否相似而非简单的名称是否相似
2.3.2.1 class
在class中,拥有相同成员的类可以相互使用,如:
class Point1 {name: string = '1'age: number = 1
}class Point2 {name: string = '2'age: number = 2
}const D1: Point1 = new Point2()
const D2: Point2 = new Point1()
也可以将至少包含被赋值的类的成员的类赋值给该类,即可以将多成员类赋值给少成员类,但至少要包含他里面的所有成员,如:
class Point1 {name: string = '1'age: number = 1
}class Point2 {name: string = '2'age: number = 2hobby: string = '打篮球'
}const D1: Point1 = new Point2()
// const D2: Point2 = new Point1() 这是错误的
2.3.2.2 接口
在接口中同理,直接看示例:
interface Point3 {name: stringage: number
}interface Point4 {name:stringage: number
}let D10: Point3 = {name: '', age: 18}
let D20: Point4 = D10
注意:类与接口之间也满足该规则,示例:
class Point8 {name: string = '张三'age: number = 18
}interface Point9 {name: stringage: number
}let D4: Point9 = new Point8()
class Point8 {name: string = '张三'age: number = 18hobby: string = '打篮球'
}interface Point9 {name: stringage: number
}let D4: Point9 = new Point8()
// let D5: Point9 = {name: '', age: 18}
// let D6: Point8 = D5 这是错误的
2.3.2.3 函数
参数个数:参数少的可以赋值给参数多的
参数类型:相同位置的参数类型要相同(原始类型)或兼容(对象类型)
若传入对象类型,则此处的兼容,不是按照类或接口的规则来看,而是把这个对象的属性看成是参数,因此遵循 “参数少的可以赋值给参数多” 的这条规则
返回值类型:
1.原始类型:相同数据类型可以互相赋值
2.对象类型:遵循成员多的赋值给成员少的
2.3.3 交叉类型
符号:&
作用:组合多种类型成一个类型(常用于对象类型)
示例:
interface Point01 {name: string
}interface Point02{age: number
}type Point03 = Point01 & Point02let obj: Point03 = {name: '',age: 18
}
2.3.3.1 交叉类型(&)和接口继承对比(extends)
相同点:都可以实现对象类型的组合
不同点:对于同名不同类型的属性,使用接口继承会报错,而使用交叉类型会变成never类型,即无法赋值,也相当于错误
但是在函数参数值类型冲突时,交叉类型会兼容两种冲突类型
interface A {// name: stringfn: (value: number) => string
}interface B {// name: numberfn: (value: string) => string
}type C = A & Blet D:C = {fn(value: number | string): string {return ''}
}
2.3.4 泛型
示例:Type可以是任意合法标识符
function method<Type> (value: Type): Type {return value
}method<number>(1)method<string>('1')method<boolean>(true)
注意:上述代码中在调用函数时写的<类型>可以省略,省略后,会由类型推断机制来推断类型,如:
function method<Type> (value: Type): Type {return value
}let a = method<number>(1)
此时a的类型为number,但是要注意的是,method的类型会变成字面量类型,但这并不影响什么
注意:原则是能省则省,省略不了还需显示传入类型
2.3.4.1 泛型约束
方法一:指定更加具体的类型
function A<Type>(value: Type[]):void {console.log(value.length)
}A(['2', 1])
方法二:extends添加约束
约束传进来的类型:
function id<T extends string | number> (value: T): T {return value
}id(1)
id('1')
// id(true) 错误写法
约束传进来的值必须拥有某属性:
function id<T extends {length: number}> (value: T): T {return value
}id([])
id('')
id({length: 10})
// id({}) 错误写法
// id(1) 错误写法
2.3.4.2 多泛型变量
示例:第一个参数可以是任意类型,而第二个参数被约束只能是string或number类型
function id<Type, Key extends string | number> (value1: Type, value2: Key) {return '1'
}id(1, 1)
id(1, '1')
// id(1, false) 错误写法
keyof 的作用:
// 定义一个接口
interface Person {name: string;age: number;isStudent: boolean;
}// 使用 keyof 获取 Person 接口的所有属性名组成的联合类型
type PersonKeys = keyof Person;// PersonKeys 等价于 'name' | 'age' | 'isStudent'
根据第一个参数数据类型来约束第二个参数数据类型:
function A<Type,Key extends keyof Type> (value1: Type, value2: Key) {return '1'
}A({name: '张三', age: 18}, 'age')
A(2, 'toFixed')
A([], 'forEach')
2.3.4.3 泛型接口
接口中也可以使用泛型,但要注意必须显示传入类型
示例:
interface Point<Type> {move: (value: Type) => Type
}const obj: Point<number> = {move(value){return value}
}
obj.move(1)
数组类型其实就是一个泛型接口
2.3.4.4 泛型类
泛型类可以选择是否传入类型
class Point<Type> {name: Typeconstructor(name: Type){this.name = name}
}let obj = new Point(1)
let obj2 = new Point('1')
2.3.4.5 泛型工具
2.3.4.5.1 Partial
作用:复制一份原有类型,并且将其所有属性或方法变成可选项
示例:
interface Point {name: stringage: number
}type TypeA = Partial<yiu>let aoo: Point = {name: '',age: 1// 所有属性必填
}let boo: TypeA = {} // 所有属性选填
2.3.4.5.2 Readonly
作用:复制一份原有类型,并将其所有属性或方法变成只读项
示例:
interface Point {name: string,age: number
}type TypeA = Readonly<Point>let obj: Point = {name: '张三',age: 18
}obj.name = '李四'let obj2: TypeA = {name: '张三',age: 18
}// obj2.name = '李四' 这是错误的
2.3.4.5.3 Pick
作用:从原有类型中挑出指定属性或方法,来构成一个新类型
示例:
interface Point {name: string,age: number,hobby: stringmove: (value: number) => string
}type TypeA = Pick<Point, 'name' | 'age' | 'move'>let obj: TypeA = {name: '张三',age: 18,move(value){return ''}
}
2.3.4.5.4 Record
作用:快速创建属性类型都相同的类型
示例:
type TypeA = Record<'name' | 'age' | 'sss', number>
// 所有属性类型都是 number
type TypeB = Record<'name' | 'age' | 'sss', number | string>
// 所有属性类型都是 number|string
2.3.5 索引签名类型
使用场景:无法确定对象中有哪些属性时使用
小知识:js对象中的键是string类型的
示例:
interface point<Type> {[Key: string]: Type
}let obj: point<number> = {a: 1,b: 2,c: 333
}
2.3.6 映射类型
基于联合类型来生成相对应的对象属性
注意:只能用于类型别名,不能用于接口
示例:
type TypeA = 'x' | 'y' | 'z'
type TypeB = {[Key in TypeA]: number
}
也可以基于对象类型来生成相对于的对象属性
使用keyof:获取对象类型中所有键的联合类型
示例:
type TypeA = {name: string,age: number
}
type TypeB = {[Key in keyof TypeA]: number
}
2.3.6.1 索引查询
作用:查询属性的类型
写法:类似于js中访问对象属性一样
示例:
type Prop = {a: string,b: number
}type TypeA = Prop['a']
同时查询多个类型:
type Prop = {a: string,b: number
}type typeA = Prop['a' | 'b']
type typeB = Prop[keyof Prop]
3 类型声明文件
3.1 介绍
ts文件在编译成js文件后,类型等相关代码都会消失,但是.d.ts文件(类型声明文件)则不会消失,可以继续提示类型
3.2 已有的类型声明文件
内置类型声明文件:
TS为JS运行时可用的所有标准化内置API都提供了声明文件
第三方库的类型声明文件:
使用npm i 安装第三方库后,导入后会显示缺失头文件
GitHub - DefinitelyTyped/DefinitelyTyped:高质量 TypeScript 类型定义的存储库。
此库提供了高质量的常用的第三方库的类型声明文件
可以通过以下命令来获取:
npm i -D @types/包名
3.3 编写类型声明文件
1.项目内共享类型:
多个ts文件用到同一种类型时,可以封装成.d.ts文件
// c.d.tsinterface Point {name: string,age: number
}export { Point }
// a.tsimport { Point } from "./c"let obj1: Point = {name: '张三',age: 18
}
// b.tsimport { Point } from "./c"let obj2: Point = {name: '李四',age: 19
}
2.为已有js文件提供类型声明
ts项目中也可以使用js文件,在导入js文件时,会自动找与其同名的.d.ts文件,以提供类型声明
declare关键字作用:
在ts中为其他地方已经存在的变量声明类型,以告知ts并不是创建一个新的变量
示例:
// a.tsimport { name, obj, move, go, Point } from "./b"console.log(name, obj)
move(1)
go(1)let obj2: Point = {name: '张三',age: 18
}
// b.jslet name = '张三'
let obj = {name: '李四',age: 18
}
function move(value) {console.log(value)return value
}const go = (value) => {console.log(value)return value
}export { name, obj, move, go }
// b.d.tsdeclare let name: string//写法一:
interface Point {name: string,age: number
}
declare let obj: Point
// 写法二:
// declare let obj: {name: string, age: number}declare function move(value: number): numbertype func = (value: number) => number
declare const go: funcexport { name, obj, move, go, Point }
此后用react做演示的例子暂时停更,因为react还没学...