Type Script

TypeScript

1、TypeScript开发环境搭建

1、下载Node.js

2、安装Node.js

3、使用npm全局安装typescript

​ ①、进入命令行

​ ②、输入:npm i -g typescript

4、创建一个ts文件

5、使用tsc对他说文件进行编译

​ ①、进入命令行

​ ②、进入ts文件所在目录

​ ③、执行命令:tsc xxx.ts

2、基本类型

类型声明:

1、类型声明是TS非常重要的一个特点

2、通过类型声明可以指定TS中变量(参数、形参)的类型

3、指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错

4、类型声明给变量设置了类型,使得变量之恶能存储某种类型的值

语法:

let 变量: 类型;
let 变量: 类型 =;
function fn(参数:类型,参数:类型):类型{
    ...
}

实例:

let a: number = 10//指定a变量类型为number

// 如果变量的声明和赋值是同时进行的,TS可以自动对变量进行类型检测
let c = true;
// 为函数参数进行变量指定
function sum(a:number,b:number):number{//指定函数返回值
    return a+b;
}
let res = sum(123,456);//只能传入两个参数,不能多传或少传

注意:

  • 1、当对变量的声明和赋值时同时进行的,TS编译器会自动判断变量的类型
  • 2、如果你的变量的声明是同时进行的,可以省略掉类型声明

类型:

类型例子描述
number1,2,3任意数字
string‘hi’,“hi”任意字符串
booleantrue,false布尔值true或false
字面量其本身限制变量的值就是该字面量的值
any*任意类型
unknown*类型安全的any
void空值(undefined)没有值(或undefined)
never没有值不能是任何值
object{name:‘张三’}任意的JS对象
array[1,2,3]任意JS数组
tuple[4,5]元组,TS新增类型,固定长度数组
enumenum{A,B}枚举,TS中新增类型

字面量:

限制变量的值就是该字面量的值

let b:"male"|"female"
b = "male",
b = "female"
//可以指定多个变量类型
let c:boolean | string
c = true;
c = 'hello';
let str1 = 'Hello TS'
const str2 = 'HELLO TS'

通过TS类型推论机制:

  • 1、str1的类型为:string
  • 2、str2的类型为:‘HELLO TS’

解释:str2是一个常量,它的值不能变化只能是"HELLO TS", 所以它的类型为:‘HELLO TS’。此处就为一个字面量类型。也就是说某个特定的字符串也可以作为TS中的类型

**使用模式:**字面量类型配合联合类型一起使用

**使用场景:**用来表示一组明确的可选值列表

比如:在贪吃蛇游戏中,游戏的方向的可选值只能是上、下、左、右中的任意一个

function changeDirection(direction:'up'|'down'|'left'|'right'){
    console.log(direction)
}

解释:参数的direction的值只能是 up/down/left/right中的任意一个

优势:相比于string类型,使用字面量类型更加精确、严谨

any:

1、any表示的是任意类型,一个变量设置类型为any后相当于该变量关闭了TS的类型检测

2、声明变量如果不指定类型,则TS解析器会自动判断变量的类型为any(隐式的any)函数参数不设置类型也会隐式具有any类型

let d;
d = 10;
d = 'hello';

3、any的类型可以赋值给任意变量

let d:any;
let s:string;
s = d;

unknown:

表示未知类型的值(遇到类型不确定的变量时尽量用unknown)

实际上就是一个类型安全的any,不能直接赋值给其他变量

//需要做类型判断才能赋值
let s:unknown = 'hello';
let e:string = 'world';
if(typeof e === "string"){
    s = e;
}
// 类型断言,可以用来告诉解析器变量的实际类型
//语法:1、变量 as 变量
//	   2、<类型>变量
s = e as string
s = <string>e;

void:

函数默认返回值为void,但可以根据return的值自动判断类型,

如果需要指定函数没有返回值时,指定返回值类型为void

void用来表示空,以函数为例,就表示没有返回值的函数

// void
function fn():void{
    
}

never:

// never表示永远不会返回结果
function fn2():never{
    throw new Error('报错')
}

object:(包含数组、对象、函数等对象)

特点:对象类型,在TS中更加细化,每个具体的对象都有自己的类型语法

表示一个js对象

{}用来指定对象中可以包含哪些属性

语法:{属性名:属性值,属性名:属性值}

// object 表示一个js对象
let f:object;
f = {};

let g:{name:string};

g = {name:'lisi'}

Number:

Number对象属性:

属性描述
MAX_VALUE可表示的最大的数,MAX_VALUE 属性值接近于 1.79E+308。大于 MAX_VALUE 的值代表 “Infinity”。
MIN_VALUE可表示的最小的数,即最接近 0 的正数 (实际上不会变成 0)。最大的负数是 -MIN_VALUE,MIN_VALUE 的值约为 5e-324。小于 MIN_VALUE (“underflow values”) 的值将会转换为 0。
NaN非数字值(Not-A-Number)。
NEGATIVE_INFINITY负无穷大,溢出时返回该值。该值小于 MIN_VALUE。
POSITIVE_INFINITY正无穷大,溢出时返回该值。该值大于 MAX_VALUE。
prototypeNumber 对象的静态属性。使您有能力向对象添加属性和方法。
constructor返回对创建此对象的 Number 函数的引用。

Number对象方法:

String:

String对象属性:

属性描述
constructor对创建该对象的函数的引用。
length返回字符串的长度。
prototype允许您向对象添加属性和方法。

String方法:

方法描述
charAt()返回在指定位置的字符。
charCodeAt()返回在指定的位置的字符的 Unicode 编码。
concat()连接两个或更多字符串,并返回新的字符串。
indexOf()返回某个指定的字符串值在字符串中首次出现的位置。
lastIndexOf()从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置。
localeCompare()用本地特定的顺序来比较两个字符串。
match()查找找到一个或多个正则表达式的匹配。
replace()替换与正则表达式匹配的子串
search()检索与正则表达式相匹配的值
slice()提取字符串的片断,并在新的字符串中返回被提取的部分。
split()把字符串分割为子字符串数组。
substr()从起始索引号提取字符串中指定数目的字符。
substring()提取字符串中两个指定的索引号之间的字符。
toLocaleLowerCase()根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射。
toLocaleUpperCase()据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射。
toLowerCase()把字符串转换为小写。
toString()返回字符串。
toUpperCase()把字符串转换为大写。
valueOf()返回指定字符串对象的原始值。

数组:

数组的类型声明:

类型[ ];

数组<类型>

//string[] 表示字符串数组
let e: string[];
e = ['a','b','c'];
//number[]表示数值数组
let f:number[] = [1,2,3];
let g:Array<number> = [1,2,3];
//需求,数组中既有number类型,又有string类型
let arr: (number|string)[] = [1,'a',3,'b']

注:| 在TS中叫做联合类型(由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种)

数组解构

将数组元素赋值给变量

var arr:number[] = [12,13] 
var[x,y] = arr // 将数组的两个元素赋值给变量 x 和 y
console.log(x) 
console.log(y)

数组迭代

var j:any; 
var nums:number[] = [1001,1002,1003,1004] 
 
for(j in nums) { 
    console.log(nums[j]) 
}

多维数组

一个数组的元素可以是另外一个数组,这样构成了多维数组。

var multi:number[][] = [[1,2,3],[23,24,25]]  
console.log(multi[0][0]) 
console.log(multi[0][1]) 
console.log(multi[0][2]) 
console.log(multi[1][0]) 
console.log(multi[1][1]) 
console.log(multi[1][2])

数组方法:

方法描述
concat()连接两个或更多的数组,并返回结果。
every()检测数值元素的每个元素是否都符合条件。
filter()检测数值元素,并返回符合条件所有元素的数组
forEach()数组每个元素都执行一次回调函数。
indexOf()搜索数组中的元素,并返回它所在的位置。如果搜索不到,返回值 -1,代表没有此项。
join()把数组的所有元素放入一个字符串。
lastIndexOf()返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
map()通过指定函数处理数组的每个元素,并返回处理后的数组。
pop()删除数组的最后一个元素并返回删除的元素。
push()向数组的末尾添加一个或更多元素,并返回新的长度。
reduce()将数组元素计算为一个值(从左到右)。
reduceRight()将数组元素计算为一个值(从右到左)。
reverse()反转数组的元素顺序。
shift()删除并返回数组的第一个元素。
slice()选取数组的的一部分,并返回一个新数组。
some()检测数组元素中是否有元素符合指定条件。
sort()对数组的元素进行排序。
splice()从数组中添加或删除元素。
toString()把数组转换为字符串,并返回结果。
unshift()向数组的开头添加一个或更多元素,并返回新的长度。

元组

元组类型是另一种类型的数组,它确切地直到包含多少个元素以及特定索引对应的类型

语法:[类型,类型]

let h:[string,string];
h = ['hello','world']

let position:[number,number] = [39.1,39.2]

解释:

  1. 元组类型可以确切地标记出有多少个元素,以及每个元素的类型
  2. 该示例中,元组有两个元素,每个元素的类型都是number

enum:

枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值

枚举:定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个

enum Gender{
    Male = 0,
    Female = 1
}

let i:{name:string,gender:Gender}
i = {
    name:'李四',
    gender:Gender.Male
}
//console.log(i.gender === Gender.Male)
let j: {name:string} & {age:number};//&表示同时
j = {name:'李四',age:18}

解释:1、使用enum关键字定义枚举

​ 2、约定枚举名称、枚举中的值以大写字母开头

​ 3、枚举中的多个值之间通过 , 分隔

​ 4、定义好枚举后,直接使用枚举名称作为类型注解

注意:1、枚举成员是有值的,默认为:从0开始自增的值

2、字符串枚举没有自增长行为,因此字符串枚举中必须设置初始化值

说明:枚举与字面量类型+联合类型组合的功能类似,都用来表示一组明确的可选值列表

Map对象:

Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。

创建map

let myMap = new Map();
let myMap = new Map([
  ["key1","value1"],
  ["key2","value2"]
])

Map 相关的函数与属性:

  • map.clear() – 移除 Map 对象的所有键/值对 。
  • map.set() – 设置键值对,返回该 Map 对象。
  • map.get() – 返回键对应的值,如果不存在,则返回 undefined。
  • map.has() – 返回一个布尔值,用于判断 Map 中是否包含键对应的值。
  • map.delete() – 删除 Map 中的元素,删除成功返回 true,失败返回 false。
  • map.size – 返回 Map 对象键/值对的数量。
  • map.keys() - 返回一个 Iterator 对象, 包含了 Map 对象中每个元素的键 。
  • map.values() – 返回一个新的Iterator对象,包含了Map对象中每个元素的值 。

迭代Map

Map 对象中的元素是按顺序插入的,我们可以迭代 Map 对象,每一次迭代返回 [key, value] 数组。

TypeScript使用for...of来实现迭代:

let nameSiteMapping = new Map();
 
nameSiteMapping.set("Google", 1);
nameSiteMapping.set("Runoob", 2);
nameSiteMapping.set("Taobao", 3);
 
// 迭代 Map 中的 key
for (let key of nameSiteMapping.keys()) {
    console.log(key);                  
}
 
// 迭代 Map 中的 value
for (let value of nameSiteMapping.values()) {
    console.log(value);                 
}
 
// 迭代 Map 中的 key => value
for (let entry of nameSiteMapping.entries()) {
    console.log(entry[0], entry[1]);   
}
 
// 使用对象解析
for (let [key, value] of nameSiteMapping) {
    console.log(key, value);            
}

类型的别名:

类型别名(自定义类型):为任意类型起别名

使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用

type myType = 1|2|3|4|5|6
let k:myType

解释:1、使用type关键字来创建类型别名

​ 2、类型别名(比如,此处的CustomArray),可以是任何合法的变量名称

​ 3、创建类型别名后,直接使用该类型别名作为变量的类型注解即可

函数类型:

函数的类型实际上指的是:函数参数和返回值的类型

为函数指定类型的两种方式:

1、单独指定参数、返回值的类型

function add(num1:number,num2:number):number{
    return num1+num2
}
const add = (num1:number,num2:number):number=>{
    return num1 + num2
}

2、同时指定参数、返回值的类型

const add:(num1:number,num2:number)=> number = (num1,num2)=>{
    return num1 + num2
}

解释:当函数作为表达式时,可以通过类似箭头函数形式的语法来为函数添加类型。

注意:这种形式只适用于函数表达式

在 TypeScript 函数里,如果我们定义了参数,则我们必须传入这些参数,除非将这些参数设置为可选,可选参数使用问号标识 ?。

3、使用函数实现某个功能时,参数可以传也可以不传。这种情况下,在给函数参数指定类型时,就用到可选参数了。比如:数组的sice方法()

function mySlice(start?:number,end?:number):void{
    console.log('起始索引',start,'结束索引',end)
}

可选参数:在可传可不传的参数名称后面添加?(问号)

注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数

4、默认参数,可以设置参数的默认值,这样在调用函数的时候,如果不传入该参数的值,则使用默认参数,语法格式为:

function calculate_discount(price:number,rate:number = 0.50) { 
    var discount = price * rate; 
    console.log("计算结果: ",discount); 
} 
calculate_discount(1000) 
calculate_discount(1000,0.30)

注意:参数不能同时设置为可选和默认。

5、剩余参数,当不知道要向函数中传入多少个参数,这时候可以使用剩余参数来定义。

//函数的最后一个命名参数 restOfName 以 ... 为前缀,它将成为一个由剩余参数组成的数组,索引值从0(包括)到 restOfName.length(不包括)。
function buildName(firstName: string, ...restOfName: string[]) {
    return firstName + " " + restOfName.join(" ");
}
  
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");

6、匿名函数,匿名函数是一个没有函数名的函数,匿名函数在程序运行时动态声明,且可以将匿名函数赋值给一个变量。

var msg = function() { 
    return "hello world";  
} 
console.log(msg())
//
var res = function(a:number,b:number) { 
    return a*b;  
}; 
console.log(res(12,2))

**匿名函数自调用:**匿名函数自调用在函数后使用 ()

(function(){
  var x = "a";
  console.log(x)
})()

7、构造函数:TypeScript 也支持使用 JavaScript 内置的构造函数 Function() 来定义函数

var res = new Function ([arg1[, arg2[, ...argN]],] functionBody)
//参数说明:

//arg1, arg2, ... argN:参数列表。
//functionBody:一个含有包括函数定义的 JavaScript 语句的字符串。
var myFunction = new Function("a", "b", "return a * b"); 
var x = myFunction(4, 3); 
console.log(x);

8、递归函数:递归函数即在函数内调用函数本身

function factorial(number) {
    if (number <= 0) {         // 停止执行
        return 1; 
    } else {     
        return (number * factorial(number - 1));     // 调用自身
    } 
}; 
console.log(factorial(6));      // 输出 720

9、Lambda函数,也称为箭头函数。

var foo = (x:number)=>10+x
console.log(foo(100))      //输出结果为 110
//
var foo = (x:number)=> {    
    x = 10 + x 
    console.log(x)  
} 
foo(100)
//可以不指定函数的参数类型,通过函数内来推断参数类型
var func = (x)=> { 
    if(typeof x=="number") { 
        console.log(x+" 是一个数字") 
    } else if(typeof x=="string") { 
        console.log(x+" 是一个字符串") 
    }  
} 
func(12) 
func("Tom")

函数重载:重载时方法名字相同,而参数不同,返回类型也可以相同也可以不同,每个重载的方法都必须有一个独一无二的参数类型列表

//参数类型不同
function disp(string):void; 
function disp(number):void;
//参数数量不同
function disp(n1:number):void; 
function disp(x:number,y:number):void;
//参数类型顺序不同
function disp(n1:number,s1:string):void; 
function disp(s:string,n:number):void;

如果参数类型不同,则参数类型应设置为 any

参数数量不同你可以将不同的参数设置为可选。

实例:

function disp(s1:string):void; 
function disp(n1:number,s1:string):void; 
function disp(x:any,y?any):void{
  	console.log(x); 
    console.log(y);
}
disp("abc") 
disp(1,"xyz");

对象类型:

JS中的对象是由属性和方法构成的,而TS中对象的类型就是在描述对象的结构(有什么类型的属性和方法)

对象类型的写法:

let person:{name:string; age:number; sayHi(name:string):void} = {
    name:'jack',
    age:19,
    sayHi(name){}
}

解释:1、直接使用{} 来描述对象结构。属性采用属性名:类型 的形式;方法采用方法名():返回值类型的形式

​ 2、如果方法有参数,就在方法名后面的小括号中指定参数类型(比如:greet(name:string):void)

​ 3、在一行代码中指定对象的多个类型属性时,使用 ; 来分隔

可选属性:

对象的属性或方法,也可以是可选的,此时就用到可选属性

function myAxios(config:{url:string;method?:string}){
    console.log(config)
}

可选属性的语法与函数可选参数的语法一致,都使用 ? (问号)来表示

鸭子类型

鸭子类型(英语:duck typing)是动态类型的一种风格,是多态(polymorphism)的一种形式。

在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。

在鸭子类型中,关注点在于对象的行为能做什么,而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

interface IPoint { 
    x:number 
    y:number 
} 
function addPoints(p1:IPoint,p2:IPoint):IPoint { 
    var x = p1.x + p2.x 
    var y = p1.y + p2.y 
    return {x:x,y:y} 
} 
 
// 正确
var newPoint = addPoints({x:3,y:4},{x:5,y:1})  
 
// 错误 
var newPoint2 = addPoints({x:1},{x:4,y:3})

接口:

接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。

当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的

解释:1、使用interface关键字来声明接口

​ 2、接口名称(比如,此处的IPerson),可以是任何合法的变量名称

​ 3、声明接口后,直接使用接口名称作为变量的类型

​ 4、因为每一行只有一个属性类型,因此,属性类型后没有 ;

interface IPerson{
    name:string
    age:number
    sayHi():void
}
let person:IPerson = {
    name:'jack',
    age:19,
    sayHi(){}
}

interface(接口)和type(类型别名)的对比:

相同点:都可以给对象指定类型

不同点:

  1. 接口,只能为对象指定类型
  2. 类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名。
//接口
interface IPerson{
    name:string
    age:number
    sayHi():void
}
//类型别名
type IPerson = {
    name:string
    age:number
    sayHi():void
}
type NumStr = number | string

接口的继承:

如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽离出来,通过继承来实现复用。

比如:

interface Point2D{x:number;y:number}
interface Point3D{x:number;y:number;z:number}
//更好的方法
interface Point2D{x:number;y:numer}
interface Point3D extends Ponit2D{z:number}

解释:

  1. 使用extends(继承)关键字实现了接口Point3D继承Point2D
  2. 继承后,Point3D就有了Point2D的所有属性和方法(此时,Point3D同时有x、y、z、三个属性)

类型断言

类型断言用来指定更具体的类型

<a href="" id="link"></a>
const aLink = document.getElementById('link') as HTMLAnchorElement

解释:

  1. 使用as关键字实现类型断言
  2. 关键字as后面的类型是一个更加具体的类型(HTMLAnchorElement是HTMLElement的子类型)
  3. 通过类型断言,alink的类型变得更加具体,这样就可以访问a变迁特有的属性或方法了
//另一种语法
const alink = <HTMLAnchorElement>document.getElementById('link')

TypeScript联合类型

联合类型(Union Types)可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。

注意:只能赋值指定的类型,如果赋值其它类型就会报错。

创建联合类型的语法格式如下:

Type1|Type2|Type3 

typeof操作符

TS提供typeof操作符:可以在类型上下文中引用变量或属性的类型(类型查询)

使用场景:根据已有变量的值,获取该值的类型,来简化类型书写

let p = {x:1,y:2}
function formatPoint(point:{x:number;y:number}){}
formatPoint(p)
function formatPoint(point:typeof p){}

解释:1、使用typeof操作符来获取变量p的类型,结果与第一种(对象字面量形式的类型)相同

​ 2、typeof出现在类型注解的位置(参数名称的冒号后面)所处的环境就在类型上下文(区别于JS代码)

​ 3、注意:typeof只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)

3、高级类型

class类

TS全面支持ES2015中引入的class关键字,并为其添加了类型注解和其他语法

class的基本使用:

class Person{}
const p = new Person()

解释:

  1. 根据TS中的类型推论,可以知道Person类的实例对象p的类型是Person
  2. TS中的class,不仅提供了class的语法功能,也作为一种类型存在

类的构造函数:

class Person{
    age:number
    gender:string
    
    constructor(age:number,gender:string){
        this.age = age
        this.gender = gender
    }
}

解释:

  1. 成员初始化后,才可以通过this.age来访问实例成员
  2. 需要为构造函数指定类型注解,否则会被隐式推断为any;构造函数不需要返回值类型

类的实例方法:

class Point{
    x = 10
    y = 10
    scale(n:number):void{
        this.x *= n
        this.y *= n
    }
}

解释:方法的类型注解()参数和返回值与函数用法相同

类的继承:

1、extends(继承父类)

说明:JS中只有extends,而implements是TS提供的

class Animal{
    move(){
        console.log('Moving along !')
    }
}
class Dog extends Animal{
    bark(){
        console.log('wang')
    }
}
const dog = new Dog()
dog.bark
dog.move

解释:

  1. 通过extends关键字实现继承
  2. 子类Dog继承父类Animal,则Dog的实例对象dog就同时具有了父类Animal和子类Dog的所有属性和方法

2、implements(实现接口)

interface Singable{
    sing():void
}
class Person implements Singable{
    sing(){
        console.log('小苹果')
    }
}

解释:

  1. 通过implements关键字让class实现接口
  2. Person类实现接口Singable意味着,Person类中必须提供Singable接口中指定的所有方法和属性

类成员的可见性 :

可以使用TS来控制class的方法或属性对于class外的代码是否可见

可见性修饰符包括:

  • public(公有的)
//public 表示公开的、公有的,公有成员可以被任何地方访问,默认可见性
class Animal{
    public move(){
        console,log('Moving along!')
    }
}
//解释:1、在类属性或方法前加public关键字,来修饰该属性或方法是公有的
	// 2、因为public是默认可见性,所以可以直接省略
  • protected(受保护的)
//protected 表示受保护的,仅对其声明所在类的子类中(非实例对象)可见
class Animal{
    protected move(){
        console,log('Moving along!')
    }
}
class Dog extends Animal{
    bark(){
        console.loh('wang')
        this.move()
    }
}
//解释:1、在类属性或方法前面添加protected关键字,来修饰该属性或方法是受保护的
//2、在子类的方法内部可以通过this来访问父类中受保护的成员,但是对实例不可见
  • private(私有的)
//private:表示私有的,只在当前类中可见,对实例对象以及子类也是不可见的
class Animal{
    private move(){
        console,log('Moving along!')
    }
    walk(){
        this.move()
    }
}
//解释:1、在类属性或方法前面添加private关键字,来修饰该属性或方法是私有的
//	   2、私有的属性或方法只在当前类中可见,对子类和实例对象也都是不可见的

static关键字

static 关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用。

class StaticMem {  
   static num:number; 
   
   static disp():void { 
      console.log("num 值为 "+ StaticMem.num) 
   } 
} 
 
StaticMem.num = 12     // 初始化静态变量
StaticMem.disp()       // 调用静态方法

instanceof运算符

instanceof 运算符用于判断对象是否是指定的类型,如果是返回 true,否则返回 false。

class Person{ } 
var obj = new Person() 
var isPerson = obj instanceof Person; 
console.log("obj 对象是 Person 类实例化来的吗? " + isPerson);

readonly(只读修饰符):

readonly:表示只读,用来防止在构造函数之外对属性进行赋值

class Person{
    readonly age:number = 18
    constructor(age:number){
        this.age = age
    }
}

解释:

  1. 使用readonly关键字修饰该属性是只读的,(只能修饰属性不能修饰方法)
  2. 注意:属性age后面的类型注解(比如,此处的number)如果不加,则age的类型为18**(字面量类型)**。
  3. 接口或者 {} 表示的对象类型,也可以使用readonly。

类型兼容性

两种类型系统: 1 StructuralType System(结构化类型系统)2Nominal Type System(标明类型系统)。TS采用的是结构化类型系统,也叫做duck typing (鸭子类型),类型检查关注的是值所具有的形状。

也就是说,在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型。

class Point {x:number;y:number}
class Point2D{x:number;y:numer}

const p: Point = new Point2D()

解释:1、Point和Point2D是两个名称不同的类

​ 2、变量p的类型被显示标注为Point类型,但是,它的值却是Point2D的实例,并且没有类型错误

​ 3、因为TS是结构化类型系统,只检查Point和Point2D的结构是否相同(属性数量和属性类型)

​ 4、但是,如果在Normal Type System中(比如:c#、java),他们就是不同的类,类型无法兼容

更准确的说法:对于对象类型来说,y的成员至少与x相同,则x兼容y(成员多的可以赋值给少的)

class Point {x:number;y:number}
class Point3D{x:number;y:number;z:number}
const p: Point = new Point3D()

解释:1、Point3D的成员至少与POint相同,则Point兼容Point3D

​ 2、所以,成员多的Point3D可以赋值给成员少的Point

接口兼容性:

interface Point{x:number;y:number}
interface Point2D {x:number;y:number}
let p1:Point
let p2:Point2D = p1
interface Point3D {x:number;y:number; z:number}
let p3:Point3D
p2 = p3
//类和接口也是兼容的
class Point4D{
    x:number
    y:number
    z:number
}
p2 = new Point4D()

函数兼容性:

函数之间兼容性比较复杂,需要考虑:

1、参数个数 ,参数多的兼容参数少的(参数少的可以赋值给参数多的

type F1 = (a:number) => void
type F2 = (a:number,b:number) => void
let f1:F1
let f2:F2 = f1

解释:1、参数少的可以赋值给参数多的,所以f1可以赋值给f2

​ 2、数组forEach方法的第一个参数是回调函数

3、在JS中省略用不到的参数实际上是很常见的,这样的使用方式,促成了TS中函数类型之间的兼容性

​ 4、并且因为回调函数是有类型的,所以,TS会自动推导出参数item、index、array的类型

2、参数类型,相同位置的参数类型要相同(原始类型)或兼容(对象类型)

type F1 = (a:number) => string
type F2 = (a:number) => string
let f1:F1
let f2:F2 = f1
//解释:函数类型F2兼容函数类型F1,因为F
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 f2:F2
let f3:F3 = f2
//解释:1、注意,此处与前面讲到的接口兼容性冲突
//	   2、技巧:将对象拆开,把每个属性看做一个个参数,则,参数少的 f2 可以赋值给参数多的f3

3、返回值类型,只关注返回值类型本身即可

type F5 = ()=>string
type F6 = ()=>string
let f5:F5
let f6:F6 = f5
type F7 = ()=>{name:string}
type F8 = ()=>{name:string; age:number}
let f7: F7
let f8: F8
f7 = f8

解释:1、如果返回值类型是原始类型,此时两个类型要相同,比如,F5和F6

​ 2、如果返回值类型是对象类型,此时成员多的可以赋值给成员少的

交叉类型

交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)

interface Person {name:string}
interface Contact {phone:string}
type PersonDetail = Person & Contact
let obj: PersonDetail = {
    name:'jack',
    phone:'133'
}

解释:使用交叉类型后,新的类型PersonDetail就同时具备了Person和Contact的所有属性类型

交叉类型(&)和接口继承(extends)的对比:

相同点:都可以实现对象类型的组合

不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同

interface A{
    fn:(value:number) => string
}
interface B extends A {//此处B处会报错(类型不兼容)
    fn:(value:String)=>string
}

interface C {
    fn:(value:string)=>string
}
type D = A & C;//此处不会报错
//可以简单理解为:
fn:(value:string | number)=>string

泛型和keyof

泛型是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class中

需求:创建一个id函数,传入什么数据就返回该函数本身(也就是说,参数和返回值类型相同)

//创建泛型函数:
function id<Type>(value:Type):Type{return value}

//调用泛型函数
const num = id<number>(10)

解释:1、语法:在函数名称的后面添加<>(尖括号),尖括号中添加类型变量,比如此处的Type

​ 2、类型变量Type,是一种特殊类型的变量,它处理类型而不是值

​ 3、该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)

​ 4、因为Type是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型

​ 5、类型变量Type,可以是任意合法的变量名称

泛型在保证类型 安全(不丢失类型信息)的同时,可以让函数等于多种不同的类型一起工作,灵活可复用。

简化调用泛型函数:

function id<Type>(value:Type):Type{
    return value
}
let num = id(10)

解释:1、在调用泛型函数时,可以省略<类型>来简化泛型函数的调用

​ 2、此时,TS内部会采用一种叫做类型参数推断的机制,来根据传入的实参自动推断出类型变量Type的类型

​ 3、比如,传入实参10,TS会自动推断出变量num的类型number,并作为Type的类型

泛型约束:

默认情况下,泛型函数的类型变量Type可以代表多个类型,这导致无法访问任何属性。比如,id(‘a’)调用函数时获取参数的长度:

function id<Type>(value:Type):Type{
    console.log(value.length)
    return value
}

解释:Type可以代表任意类型,无法保证一定存在length属性,比如number类型就没有length。此时,就需要为泛型添加约束来收缩类型(缩窄类型取值范围)

添加泛型约束收缩类型,主要有以下两种方式:

1、指定更具体的类型

function id<Type>(value:Type[]):Type[]{
    console.log(value.length)
    return value
}
//将类型修改为Type类型的数组

2、添加约束

interface ILength{length:number}
function id<Type extends ILength>(value:Type):Type{
    console.log(value.length)
    return value
}

解释:1、创建描述约束的接口ILength,该接口要求提供length属性

​ 2、通过extends关键字使用该接口,为泛型(类型变量)添加约束

​ 3、该约束表示:传入的类型必须具有length属性

注意:传入的实参(比如,数组)只要有length属性即可,这也符合前面讲到的接口的类型兼容性

泛型的类型变量可以有多个,并且类型变量之间还可以约束

function getProp<Type, key extends keyof Type>(obj:Type,key:Key){
    return obj[key]
}
let person = {name:'jack',age:18}
getProp(person,'name')

解释:1、添加了第二个类型变量key,两个类型变量之间使用(,)分隔

2、keyof关键字接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型

​ 3、本示例中keyof Type实际上获取的是person对象所有键的联合类型,也就是:‘name’|‘age’

​ 4、类型变量Key受Type约束,可以理解为:Key只能是Type所有键中的任意一个,或者说只能访问对象中存在的属性

泛型接口:

接口也可以配合泛型来使用,以增加其灵活性,增强其复用性

interface IdFunc<Type>{
    id:(value:Type)=>Type
    ids:()=>Type[]
}

let obj: IdFunc<number> = {
    id(value){return value},
    ids(){return [1,3,5]}
}

解释:1、在接口名称的后面添加<类型变量>,那么,这个接口就变成了泛型接口

​ 2、接口的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量

​ 3、使用泛型接口时,需要显式指定具体的类型(比如,此处的idFunc)。

​ 4.下hi,id方法的参数和返回值类型都是number;ids方法的返回值类型是number[]

泛型类:

class也可以配合泛型来使用

例如:react的class组件的基类Component就是泛型类,不同的组件有不同的props和state

interface IState{count:number}
interface Iprops{maxLength:number}
class InputCount extends React.Component<IProps,IState>{
    state:IState = {
        count:0
    }
    render(){
        return <div>{this.props.maxLength}</div>
    }
}
//解释:React.Component泛型类的两个类型变量,分别指定props和state类型

创建泛型类:

class GenericNumber<NumType>{
    defaultValue:NumType
    add:(x:NumType,y:NumType)=>NUmType
}

解释:1、类似于泛型接口,在class名称后面添加<类型变量>,这个类就变成了泛型类

​ 2、此处的add方法,采用的是箭头函数形式

const myNum = new GenericNumber<number>()
myNum.defaultValue = 10

类似于泛型接口,在创建class实例时,在类名后面通过<类型>来指定明确的类型

泛型工具类型:

TS内置了一些常用的 工具类型,来简化TS中的一些常见操作

Partial

用来构造(创建)一个类型,将Type的所有属性设置为可选

interface Props{
    id:string
    children:number[]
}
type PartialProps = Partial<Props>

解释:构造出来的新类型PartialProps结构和Props相同,但所有的属性都变为可选的

Readonly

用来构造一个类型,将Type的所有属性都设置为只读

interface Props{
    id:string
    children:number[]
}
type ReadonlyProps = ReadOnly<Props>

解释:构造出来的新类型ReadOnlyProps结构和Props相同,但所有属性都变为只读的

let props:ReadonlyProps = {id:'1',children:[]}
props.id = '2'//无法赋值,因为属性只读

Pick<Type,Keys>

从Type中选择一组属性来构造新类型

interface Props{
    id:string
    title:string
    children:number[]
}
type PickProps = Pick<Props, 'id'|'title'>

解释:

  1. Pick工具类有两个类型变量:1表示选择谁的属性 2表示选择哪几个属性
  2. 其中第二个类型变量,如果只选择一个则只传入该属性名即可
  3. 第二个类型变量传入的属性只能是第一个类型变量中存在的属性
  4. 构造出来的新类型PickProps,只有id和title两个属性类型

Record<Keys,Type>

构造一个对象类型,属性键为Keys,属性类型为Type

type RecordObj = Record<'a'|'b'|'c',string[]>
let obj:RecordObj = {
    a:['1'],
    b:['2'],
    c:['3']
}

解释:

  1. Record工具类型有两个类型变量:1表示对象有哪些属性2表示对象属性的类型
  2. 构建的新对象类型RecordObj表示:这个对象有三个属性分别为a/b/c,属性值的类型都是string[ ]。

索引签名类型和索引查询类型

绝大多数情况下,我们都可以在使用对象前就确定对象的结构,并为对象添加准确的类型。

使用场景:当无法确定对象中有哪些属性(或者说对象中可以出现任意多个属性),此时,就用到索引签名类型了。

interface AnyObject{
    [key:string]:number
}
let obj: AnyObject = {
    a:1,
    b:2,
}

解释:

1.使用[key: string]来约束该接口中允许出现的属性名称。表示只要是string类型的属性名称,都可以出现在对象中。

2.这样,对象obj中就可以出现任意多个属性(比如,a、b等)。

3.**key只是一个占位符,**可以换成任意合法的变量名称。

4.隐藏的前置知识:JS中对象({})的键是string类型的。

映射类型

映射类型: 基于旧类型创建新类型(对象类型),减少重复、提升开发效率。

比如,类型PropKeys有x/y/z,另一个类型Type1中也有x/y/z,并且Type1中x/y/z的类型相同:

type PropKeys = 'x'|'y'|'z'
type Type1 = { x: number; y: number; z: number}

这样书写没错,但x/y/z重复书写了两次。像这种情况,就可以使用映射类型来进行简化。

type PropKeys = 'x'|'y'|'z'
type Type2 = {[Key in PropKeys]:number}

解释:

  1. 映射类型是基于索引签名类型的,所以,该语法类似于索引签名类型,也使用了。
  2. Key in PropKeys,表示Key可以是PropKeys联合类型中的任意一个,类似于forin(let k in obj)。
  3. 使用映射类型创建的新对象类型 Type2和类型 Type1结构完全相同。
  4. 注意:映射类型只能在类型别名中使用,不能在接口中使用。

映射类型除了根据联合类型创建新类型外,还可以根据对象类型来创建:

type Props = {a:number;b:string;c:boolean}
type Type3 = {[key in keyof Props]:number}

解释:

  1. 首先,先执行keyof Props 获取到对象类型Props中所有键的联合类型即,‘a’|‘b’|‘c’
  2. 然后,Key in …就表示Key可以是Props中所有的键名称中任意一个

命名空间

命名空间一个最明确的目的就是解决重名问题。

命名空间定义了标识符的可见范围,一个标识符可在多个名字空间中定义,它在不同名字空间中的含义是互不相干的。这样,在一个新的名字空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其他名字空间中。

namespace:

namespace SomeNameSpaceName { 
   export interface ISomeInterfaceName {      }  
   export class SomeClassName {      }  
}

以上定义了一个命名空间 SomeNameSpaceName,如果我们需要在外部可以调用 SomeNameSpaceName 中的类和接口,则需要在类和接口添加 export 关键字。

在另一个命名空间调用语法格式为:

SomeNameSpaceName.SomeClassName;

如果一个命名空间在一个单独的 TypeScript 文件中,则应使用三斜杠 /// 引用它,语法格式如下:

/// <reference path = "SomeFileName.ts" />

嵌套命名空间

命名空间支持嵌套,即你可以将命名空间定义在另外一个命名空间里头。

namespace namespace_name1 { 
    export namespace namespace_name2 {
        export class class_name {    } 
    } 
}

成员的访问使用点号 . 来实现

模块

TypeScript 模块的设计理念是可以更换的组织代码。

模块是在其自身的作用域里执行,并不是在全局作用域,这意味着定义在模块里面的变量、函数和类等在模块外部是不可见的,除非明确

地使用 export 导出它们。类似地,我们必须通过 import 导入其他模块导出的变量、函数、类等。

两个模块之间的关系是通过在文件级别上使用 import 和 export 建立的。

模块导出使用关键字 export 关键字,语法格式如下:

// 文件名 : SomeInterface.ts 
export interface SomeInterface { 
   // 代码部分
}

要在另外一个文件使用该模块就需要使用 import 关键字来导入:

import someInterfaceRef = require("./SomeInterface");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值