npm i -g typescript
tsc -v
Version 5.1.6
示例代码:
let age:number = 18
注意:说明上序代码中的 :number 就是类型注解
作用:为代码添加类型约束,约定了是什么类型就只能给该类型付同类型的值。否则会报错!

1、数组类型的两种写法,推荐用第一种
- let arrNumber: number[] = [1, 2, 3]
- let arrString: Array<string> = ['1', '2', '3']
注意:一个数组中存在多个类型的数据
- //加上小括号说明,数组内的类型可以是小括号里的任何类型
- let arr: (number | string | null)[] = [null, 1, '23']
-
- //不加小括号,说明arr即可以是Number类型,也可以是string类型的数组
- let arr2: number | string[] = 123
- let arr3: number | string[] = ['1', '2', '3']
解释:|在ts中叫联合类型
- type CustomArray = (number | string)[]
- let arr1: CustomArray = ['1', 1]
- let arr2: CustomArray = ['1', 1]
注意:
注意:
- function add(number1:number,number2:number):number{
- return number1 + number2
- }
-
- console.log(add(1,2));
- let add = (number1: number, number2: number): number => {
- return number1 + number2
- }
- console.log(add(1, 3));
注意:这种方法只能用于函数表达式

- let add: (number1: number, number2: number) => number = (number1, number2) => {
- return number1 + number2
- }
- console.log(add(1, 3));
注意:如果函数没有返回值,那么函数返回值就是void(就是空值的意思)
- function onVoid(num:number):void{
- console.log(num);//执行结果:12
- }
-
- onVoid(12)
注意:使用函数实现某个功能时,函数参数可传可不传。这种情况在给函数指定参数类型时,就用到可选参数,
可选参数:在可传可不传的参数后面加 ?
注意:可选参数后面不能再写必传参数
- function mySlice(start?: number, end?: number) {
- console.log('开始', start, '结束', end);
- }
-
- mySlice()//开始 undefined 结束 undefined
- mySlice(1)//开始 1 结束 undefined
- mySlice(1, 2)//开始 1 结束 2
注意:js中的对象是由属性和方法构成的,而TS中对象的类型就是在描述对象的结构(有什么类型的属性和方法)
解释:
对象类型写法:
- let obj: { name: string; age: number; onSay(): void } = {
- name: '李玉',
- age: 21,
- onSay() {
- console.log('111');
- }
- }
-
- let obj2: {
- name: string
- age: number
- onSay: (e:number) => void
- } = {
- name: '简隋英',
- age: 28,
- onSay(e) {
- console.log(e);
- }
- }
注意:
- function myAxios(config: { url: string, methods?: object }) {
-
- }
- let config = {
- url:'https://www.baidu.com'
- }
- myAxios(config)
注意:当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的效果。
解释:
- interface IPeron {
- name: string
- age: number
- onSay: () => void
- }
-
- let obj: IPeron = {
- name: '李玉',
- age: 21,
- onSay() {
-
- }
- }
interface(接口)和type(类型别名)的对比
* 接口interface只能为对像指定类型
* 类型别名type,不仅可以对对象指定类型,实际上可以为任意类型指定别名
*type用=(等号),interface不用= (等号)
interface例子:
- interface IPeron {
- name: string
- age: number
- onSay: () => void
- }
-
- let obj: IPeron = {
- name: '李玉',
- age: 21,
- onSay() {
-
- }
- }
type例子:
- type IPeron = {
- name: string
- age: number
- onSay: () => void
- }
-
- let obj: IPeron = {
- name: '李玉',
- age: 21,
- onSay() {
-
- }
- }
type例子:
type NumStr = number | string
注意: 如果两个接口之间有相同的属性或方法,可以将公共属性和方法抽离出来,通过继承来实现复用。
如下列,这两个接口都有x,y属性,重复写两次太过繁琐。
- interface IPearn1 {
- x:string,
- y:number
- }
-
- interface Ipearn2{
- x:string,
- y:number,
- id:number
- }
更好的方式:使用extends关键字的方法去实现继承
- interface IPearn1 {
- x:string,
- y:number
- }
-
- interface Ipearn2 extends IPearn1{
- id:number
- }
场景:
在地图中使用经纬度来标记坐标信息。
可以使用数组来标记坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型。
let position: number[] = [12, 23]
注意:更好的方法是使用元组(Tuple),元组类型是另一种类型的数组,它确切的知道包含多少个元素,以及特定索引对应的类型。
let position: [number, number] = [12, 45]
解释:
注意:在TS中,某些没有声明却指出类型的地方,TS的类型推论机制会帮助提供类型。所以,因为有类型推论的存在,有的地方可以沈略类型注解( ; )
- let aa = 12
- let bb = 'dddd'
- function add(num1: number, num2: number) {
- return num1 + num2
- }
-
- add(1, 2)
注意:有时你会比TS更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型
比如:

注意:getElementById方法返回值的类型是HTMLElement,该类型只包含所有标签的公共属性或方法,不包含a标签特有的href等属性。
因此,这个类型太宽泛(不具体),无法超做href等a标签特有的属性的方法。
解决方式:这种情况下就需要使用类型断言指定更加具体的类型。

解释:
注意:另一种写法是,使用<>语法 
技巧:在浏览器控制台,通过console.dir()打印DOM元素,在属性列表的后面,即可看到该元素的类型。
思考以下代码,两个变量的类型分别是?

通过TS类型推论机制,可以得到答案:
解释:
注意:此处的 'Hello TS'就是字面量类型。也就是说某个特定字符串也可以作为TS中的类型。
除字符串外,任何的JS字面量(比如、对象、数字等)都可以作为类型使用。
- let str1 = 'Hello'
- const str2: 'Hello' = 'Hello'
- let age: 18 = 18
使用模式:字面量类型配合联合类型(|)一起用
使用场景:用来表示一组明确的可选值列表。
比如贪吃蛇游戏中,游戏的方向就只有(上下左右)任意一个
解释:参数direction的值只能是up/down/left/right中的任意一个。
优势:相比于string类型,使用字面量类型更加精确、严谨。
注意:形参direction的类型为枚举Direction,那么,实参的值就应该是枚举Direction成员中的任意一个。
- enum Direction { UP, Down, Left, Right }
-
- function changeDirection(direction: Direction) {
- console.log(direction);
- }
-
- changeDirection(Direction.UP)
- changeDirection(Direction.Down)
- changeDirection(Direction.Left)
- changeDirection(Direction.Right)
解释:类似于JS中的对象,直接通过点(.)语法访问枚举成员。
问题: 我们将枚举成员作为函数的实参,它的值是什么?

解释:通过将鼠标移入Direction.UP可以看到枚举成员UP的值是0
注意:枚举成员是有值的,默认为:从0开始自增的数字
我们把,枚举成员的值为数字枚举
当然也可以给枚举中的成员初始化值:
- //UP=10, Down=11, Left=12, Right=13
- enum Direction { UP = 10, Down, Left, Right }
- //UP=10, Down='abc', Left=8, Right=4
- enum Direction { UP = 10, Down = 5, Left = 8, Right = 4 }
注意:枚举成员的值是字符串
- //UP=UP, Down='Down', Left='Left', Right='Right'
- enum Direction { UP = 'UP', Down = 'Down', Left = 'Left', Right = 'Right' }
-
- function changeDirection(direction: Direction) {
- console.log(direction);
- }
-
- changeDirection(Direction.UP)
- changeDirection(Direction.Down)
- changeDirection(Direction.Left)
- changeDirection(Direction.Right)
注意:字符串枚举没有自增长行为,因此,字符串枚举成员都必须拥有初始值
枚举是TS为数不多的非JS类型级拓展(不仅仅是类型)的特征之一
因为:其他类型不仅仅被当做类型,而是枚举不仅用于类型还提供值(枚举成员都是有值的)
也就是说,其他类型会在编译成JS代码时直接移除,但是,枚举类型会被编译成JS代码。
TS:
- //UP=UP, Down='Down', Left='Left', Right='Right'
- enum Direction { UP = 'UP', Down = 'Down', Left = 'Left', Right = 'Right' }
-
- function changeDirection(direction: Direction) {
- console.log(direction);
- }
-
- changeDirection(Direction.UP)
- changeDirection(Direction.Down)
- changeDirection(Direction.Left)
- changeDirection(Direction.Right)
JS:
- //UP=UP, Down='Down', Left='Left', Right='Right'
- var Direction;
- (function (Direction) {
- Direction["UP"] = "UP";
- Direction["Down"] = "Down";
- Direction["Left"] = "Left";
- Direction["Right"] = "Right";
- // Direction为对象,UP,Down。。。为属性加赋值
- })(Direction || (Direction = {}));
- //传入Direction=understand||Direction = {}
-
- function changeDirection(direction) {
- console.log(direction);
- }
- changeDirection(Direction.UP);
- changeDirection(Direction.Down);
- changeDirection(Direction.Left);
- changeDirection(Direction.Right);
说明:枚举类型和前面说到的字面量类型+联合类型组合功能类似,都用来表示一组明确的可选值列表。
一般情况下,推荐使用字面量类型+联合类型,因为相比于枚举这种方式更加简洁、高效。
原则:不推荐使用any!这会让TS变成"AnyScript"(失去类型保护的优势)
因为当值的类型为any时,可以对该值进行任意超做,并且不会有任何提示。

其他隐式具有any类型的情况:
众所周知,JS提供了typeof运算符,用来在js中获取数据的类型
console.log(typeof 'hello'); //打印 string
实际上TS也提供了typeof操作符:可以在类型上下文中引用变量或属性的类型(类型查询)。
使用场景:根据已有变量的值,来获取该值的类型,来简化类型书写。
- let parameter = {
- x: 10,
- y: 20
- }
-
- function forMater(point: { x: number, y: number }) {}
-
- forMater(parameter)
- let parameter = {
- x: 10,
- y: 20
- }
-
- let p = {
- x: 0,
- y: 0
- }
-
- function forMater(point: typeof p) {}
-
- forMater(parameter)
TS全面支持ES5中引入的class关键字
- class Person{
- //添加属性的方法有以下两种
- name1:string
- name2 = '简隋英'
- }
-
- const p = new Person()
-
- p.name1//属性是string
- p.name2//属性是string
解释:
- class Person{
- //添加属性的方法有以下两种
- name1:string
- name2 = '简隋英'
-
- constructor(str1:string,str2:string){
- this.name1 = str1
- this.name2 = str2
- }
- }
-
- const p = new Person('李玉','简隋英')
-
- p.name1//属性是string
- p.name2//属性是string
解释:

- class Person {
- x = '简隋英'
- y = '李玉'
-
- scale(name1: string, name2: string): void {
- this.x = name1
- this.y = name2
- }
- }
-
- const p = new Person()
- p.scale('江停', '严峫')
-
- console.log(p.x, p.y);
解释:方法的类型注解(参数和返回值)于函数相同。
注意:类的继承有两种方式extends(继承父类)和implements(实现接口)
- class Name{
- onBackName(){
- console.log('江停');
- }
- }
-
- class Person extends Name{
-
- }
-
- const p = new Person()
- p.onBackName() //结果打印:江停
解释:
- interface Singable{
- name:string
- sing():void
- }
-
- class Person implements Singable{
- name: string
- sing(): void {
- console.log('夫妻双双把家还!');
- }
- }
解释:
类成员的可见性,可以使用TS来控制class的方法或属性对于class外的代码是否可见。
可见性修饰符包括:1、public(公有的)2、protected(受保护的)3、privated(私有的)
-
- class Person {
- name = '江停'
- public sing(name: string): void {
- console.log(this.name + name);
- }
- }
-
- const p = new Person()
- p.sing('严峫')//结果打印:江停严峫
注意:
- class Name {
- protected member() {
- console.log('吴雩');
- }
- cole() {
- this.member//这里可以调用
- }
- }
-
- const nameClass = new Name()
- nameClass.cole()//这个方法可以调用
- nameClass.member()//这个方法不可以调用
-
- class Person extends Name {
- name = '江停'
- sing(name: string): void {
- this.member()
- }
- }
-
- const p = new Person()
- p.sing('严峫')//结果打印:江停严峫
- p.cole()//这个方法可以调用
- p.member()//这个方法不可以调用
注意:
- class Name {
- private member() {
- console.log('吴雩');
- }
- cole() {
- this.member//这里可以调用
- }
- }
-
- const nameClass = new Name()
- nameClass.cole()//这个方法可以调用
- nameClass.member()//这个方法不可以调用
-
- class Person extends Name {
- name = '江停'
- sing(name: string): void {
- this.member()//这个方法不可以调用
- }
- }
-
- const p = new Person()
- p.sing('严峫')//结果打印:江停严峫
- p.cole()//这个方法可以调用
- p.member()//这个方法不可以调用
注意:
出来可见修饰符以外,还有一个常见修饰符就是:readonly(只读修饰符)
readonly:表示只读,用来防止构造函数以外的属性对他进行赋值
注意:只能在constructor中修改属性,其他地方不能。且readonly只能用于属性不能用于方法。
- class Person{
- readonly age:number = 18
- constructor(age1:number){
- this.age = age1 //只能在这里改
- }
- changAge(){
- this.age = 23 //修改属性会报错
- }
- }
-
- const p = new Person(21)
注意:如果 readonly的属性没有增加类型注解,那么值的属性就是他的初始值,相当于一个常量。就算是在constructor里也不能改。
- class Person{
- readonly age = 18
- constructor(age1:number){
- this.age = age1 //这里也不能改
- }
- changAge(){
- this.age = 23 //修改属性会报错
- }
- }
-
- const p = new Person(21)
- interface IPerson{
- readonly name:string
- }
-
- let obj:IPerson ={
- name:'严峫'
- }
-
- obj.name = '江停'//name属性标记只读,所以不能被复制
-
- let obj: { readonly name: string } = {
- name: '严峫'
- }
-
- obj.name = '江停'//name属性标记只读,所以不能被复制
解释:
两种类型系统:结构化类型系统和标明类类型系统
TS采用的是结构化类型系统,也叫做duck typing(鸭子类型),类型检查关注的是值所具备的性状
也就是说,结构化类型系统中,如果两个对象具有相同的形状,则认为他们属于同一类型。
- class Name {
- x:number
- y:number
- }
-
- class Name2{
- x:100
- y:200
- }
-
- const p:Name = new Name2()
解释:
注意:结构化类型系统中,如果两个对象具有相同的形状,则认为他们属于同一类型,这种说法不完全准确。
更准确的说法是:对于对象类型来说,y的成员至少于x相同,则认为x兼容y(成员多的可以赋值给少的)
- class Point {
- x: number
- y: number
- }
- class Point3D {
- x: number
- y: number
- z: number
- }
-
- const p: Point = new Point3D
解释:
除了class之外,TS中的其他类型也存在相互兼容的情况,包括:1、接口兼容 。2、函数兼容性等。
接口之间的兼容性,类似于class,并且,class和interface之间也可以兼容

函数之间的兼容性比较复杂,需要考虑:1、参数个数 2、参数类型 3、返回值类型
参数个数,参数多的兼容参数少的(或者说,参数少的可以赋值给多的)
- type F1 = (a:number) =>void
- type F2 = (a:number, b:number)=>void
-
- let a:number = 12
- let f1:F1 = (a) => {
- return null
- }
-
- let f2:F2 = f1
-
- const arr = ['a', 'b', 'c']
- arr.forEach(() => { })
- arr.forEach((item) => { })
解释:
参数类型:相同位置的参数类型要相同,(原始类型)或兼容(对象类型)
- type F1 = (a: number) => void
- type F2 = (a: number) => void
- let f1:F1
- let f2:F2
- let num = 12
- f2 = (num) => {}
-
- f1 = f2
- f2 = f1
- interface Point2D {
- x: number
- y: number
- }
- interface Point3D {
- x: number
- y: number
- z: number
- }
- type F2 = (p: Point2D) => void
- type F3 = (p: Point3D) => void
-
- let p1 = { x: 12, y: 10 }
- let f2: F2 = (p1) => { }
- let f3: F3 = f2
- f2 = f3//会报错,多的不能赋值给少的
-
解释:
- type F5 = () => string
- type F6 = () => string
- let f5:F5 = () => {return '111'}
- let f6:F6 = f5
- type F7 = () => { name: string }
- type F8 = () => { name: string, age: number }
- let f7: F7 = () => {
- return {
- name:'李玉'
- }
- }
- let f8: F8 = () => {
- return {
- name: '简隋英',
- age: 28
- }
- }
- f7 = f8
-
- console.log(f7);//打印结果:[Function: f8]
解释:
交叉类型(&):功能类型接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)
- interface Person{
- name:string
- }
- interface Content extends Person{
- age:number
- }
- let PersonContent:Content = {
- name:'简隋英',
- age:27
- }
- interface Person{
- name:string
- }
- interface Content{
- age:number
- }
-
- type PersonContent = Person & Content
-
- let obj:PersonContent = {
- name:'简隋英',
- age:27
- }
解释:使用交叉类型后,新的类型PersonContent就同时具备了Person 和 Content的所有属性类型。
type PersonContent = { name: string, age: number }
交叉型(&)和接口型(extends)的对比:
- interface A {
- fn: (value: number) => string
- }
-
- interface B extends A{//B不兼容
- fn: (value: string) => string
- }
- interface A {
- fn: (value: number) => string
- }
-
- interface B {//B不兼容
- fn: (value: string) => string
- }
-
- type C = A & B
说明:以上代码,接口继承会报错(类型不兼容);交叉类型没有错误,可以简单理解为:
fn: (value: string | number) => string
泛型是在保证类型安全的前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中。
需求 :创建一个id函数,传入什么数据就返回该数据本身。(也就是说参数和返回值类型相同)
- function id(value:number):number {
- return value
- }
比如:id(10)调用以上函数就会直接返回10本身。但是,该函数只能用于数值类型,无法用于其他类型。
为了能让函数接收任意类型,可以将参数类型修改为any,但是,这样就会失去TS的类型保护,类型不安全。
- function id(value:any):any {
- return value
- }
解释:泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活复用。
- function id<Type>(value: Type): Type {
- return value
- }
解释:
- function id<Type>(value: Type): Type {
- return value
- }
-
- const num1 = id<number>(10)
- const str = id<string>('江停')
- const beal = id<boolean>(true)
解释:
同样,如果传入类型string,函数id参数和返回值的类型就是string
这样,通过泛型就做到了让id函数于多种不同的类型一起工作,实现了复用的同时保证了类型安全
- function id<Type>(value: Type): Type {
- return value
- }
-
- const num1 = id(10)
- const str = id('江停')
- const beal = id(true)
解释:
推荐:使用这种简化的方式调用泛型函数,使代码变得更短,更加易读
说明:当编译器无法推断类型或者类型不准确时,就需要显式的传入类型参数
泛型约束:默认情况下,泛型函数的类型变量Type可以代表多个类型,这导致无法访问任何属性。
比如:id('a')调用函数时获取参数长度:
- function id<Type>(value: Type): Type {
- console.log(value.length);//这个代码报错
- return value
- }
-
- const num1 = id([10,12])
解释:Type可以代表任意类型,无法保证一定存在length属性,比如number类型就没有length。
此时,就需要为泛型添加约束来收缩类型(缩小类型的取值范围)
添加泛型的收缩属性,主要有以下两种方式:指定更加具体的类型 和 添加约束
- function id<Type>(value: Type[]): Type[] {
- console.log(value.length);//这个代码报错
- return value
- }
-
- const num1 = id([10,12])
比如:将类型修改为Type[](Type类型的数组)。因为只要是数组就一定存在length属性,因此就可以访问了
- interface ILength{
- length:number
- }
- function id<Type extends ILength>(value: Type): Type {
- console.log(value.length);//这个代码报错
- return value
- }
-
- const num1 = id([10,12])
- const str = id('江停')
- const beal = id({length:12,name:'江停'})
解释:
注意:传入的实参(比如,数组)只要有length属性即可,这也符合前面讲到的接口的类型兼容性
泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如:第二个类型变量受第一个类型变量约束)。
比如,创建一个函数来获取对象中属性的值:
- function getProp<Type,Key extends keyof Type>(obj:Type,key:Key){
- return obj[key]
- }
-
- let person = {name:'江停',age:31}
- console.log(getProp(person,'name'));
-
解释: