网上书店网站开发代码芜湖做网站的邓健照片

当前位置: 首页 > news >正文

网上书店网站开发代码,芜湖做网站的邓健照片,织梦网站后台文章编辑美化代码,wordpress纯代码添加海报1. any 类型#xff0c;unknown 类型#xff0c;never 类型 TypeScript 有两个“顶层类型”#xff08;any和unknown#xff09;#xff0c;但是“底层类型”只有never唯一一个 1、any 1.1 基本含义 any 类型表示没有任何限制#xff0c;该类型的变量可以赋予任意类型的…1. any 类型unknown 类型never 类型 TypeScript 有两个“顶层类型”any和unknown但是“底层类型”只有never唯一一个 1、any 1.1 基本含义 any 类型表示没有任何限制该类型的变量可以赋予任意类型的值。变量类型一旦设为anyTypeScript 实际上会关闭这个变量的类型检查。即使有明显的类型错误只要句法正确都不会报错。 let x:any;x 1; // 正确 x foo; // 正确 x true; // 正确 TypeScript 认为只要开发者使用了any类型就表示开发者想要自己来处理这些代码所以就不对any类型进行任何限制怎么使用都可以。 从集合论的角度看any类型可以看成是所有其他类型的全集包含了一切可能的类型。TypeScript 将这种类型称为“顶层类型”top type意为涵盖了所有下层。 1.1.1 类型推断问题 对于开发者没有指定类型、TypeScript 必须自己推断类型的那些变量如果无法推断出类型TypeScript 就会认为该变量的类型是any。 function add(x, y) {return x y; }add(1, [1, 2, 3]) // 不报错 上面示例中函数add()的参数变量x和y都没有足够的信息TypeScript 无法推断出它们的类型就会认为这两个变量和函数返回值的类型都是any。以至于后面就不再对函数add()进行类型检查了怎么用都可以。这显然是很糟糕的情况所以对于那些类型不明显的变量一定要显式声明类型防止被推断为any。 当声明的时候没有赋值没给类型也不会报错所以在声明的同时要赋值不然会存在安全隐患 1.1.2污染问题 ny类型除了关闭类型检查还有一个很大的问题就是它会“污染”其他变量。它可以赋值给其他任何类型的变量因为没有类型检查导致其他变量出错 let x:any hello; let y:number;y x; // 不报错y * 123 // 不报错 y.toFixed() // 不报错 上面示例中变量x的类型是any实际的值是一个字符串。变量y的类型是number表示这是一个数值变量但是它被赋值为x这时并不会报错。然后变量y继续进行各种数值运算TypeScript 也检查不出错误问题就这样留到运行时才会暴露。 污染其他具有正确类型的变量把错误留到运行时这就是不宜使用any类型的另一个主要原因。 2.unknown 与any含义相同表示类型不确定可能是任意类型但是它的使用有一些限制不像any那样自由可以视为严格版的any unknown类型跟any类型的不同之处在于它不能直接使用。主要有以下几个限制: 1.unknown类型的变量不能直接赋值给其他类型的变量除了any类型和unknown类型 let v:unknown 123;let v1:boolean v; // 报错 let v2:number v; // 报错 2. 不能直接调用unknown类型变量的方法和属性。 let v1:unknown { foo: 123 }; v1.foo // 报错let v2:unknown hello; v2.trim() // 报错let v3:unknown (n 0) n 1; v3() // 报错 3. unknown类型变量能够进行的运算是有限的只能进行比较运算运算符、、!、!、||、、?、取反运算运算符!、typeof运算符和instanceof运算符这几种其他运算都会报错 let a:unknown 1;a 1 // 报错 a 1 // 正确 4. 经过“类型缩小”unknown类型变量才可以使用。所谓“类型缩小”就是缩小unknown变量的类型范围确保不会出错。 let a:unknown 1;if (typeof a number) {let r a 10; // 正确 } unknown可以看作是更安全的any。一般来说凡是需要设为any类型的地方通常都应该优先考虑设为unknown类型。也视为所有其他类型除了any的全集所以它和any一样也属于 TypeScript 的顶层类型。 3.never 由于不存在任何属于“空类型”的值所以该类型被称为never即不可能有这样的值。 never类型的使用场景主要是在一些类型运算之中保证类型运算的完整性。另外不可能返回值的函数返回值的类型就可以写成never 如果一个变量可能有多种类型即联合类型通常需要使用分支处理每一种类型。这时处理所有可能的类型之后剩余的情况就属于never类型。   function fn(x:string|number) {if (typeof x string) {// …} else if (typeof x number) {// …} else {x; // never 类型} } never类型的一个重要特点是可以赋值给任意其他类型。 function f():never {throw new Error(Error); }let v1:number f(); // 不报错 let v2:string f(); // 不报错 let v3:boolean f(); // 不报错 上面示例中函数f()会抛错所以返回值类型可以写成never即不可能返回任何值。各种其他类型的变量都可以赋值为f()的运行结果never类型 2.类型系统 1.基本类型继承js基本数据类型 boolean string number bigint symbol object undefined null 注意 1.上面所有类型的名称都是小写字母首字母大写的Number、String、Boolean等在 JavaScript 语言中都是内置对象而不是类型名称 2.如果没有声明类型的变量被赋值为undefined或null它们的类型会被推断为any如果希望避免这种情况则需要打开编译选项strictNullChecks 2.包装对象类型 TypeScript 对五种原始类型分别提供了大写和小写两种类型: Boolean 和 boolean String 和 string Number 和 number BigInt 和 bigint Symbol 和 symbol 大写类型同时包含包装对象和字面量两种情况小写类型只包含字面量不包含包装对象 const s1:String hello; // 正确 const s2:String new String(hello); // 正确const s3:string hello; // 正确 const s4:string new String(hello); // 报错 String类型可以赋值为字符串的字面量也可以赋值为包装对象。但是string类型只能赋值为字面量赋值为包装对象就会报错。(只使用小写类型不使用大写类型) 3.Object 类型与 object 类型 3.1Oject类型 大写的Object类型代表 JavaScript 语言里面的广义对象。所有可以转成对象的值都是Object类型这囊括了几乎所有的值。(原始类型值、对象、数组、函数都是合法的Object类型) let obj:Object;//与let obj:{}一样只不过后者是简写obj true; obj hi; obj 1; obj { foo: 123 }; obj [1, 2]; obj (a:number) a 1; 除了undefined和null这两个值不能转为对象其他任何值都可以赋值给Object类型 let obj:Object;obj undefined; // 报错 obj null; // 报错 3.2object类型 小写的object类型代表 JavaScript 里面的狭义对象即可以用字面量表示的对象包含对象、数组和函数不包括原始类型的值 let obj:object;obj { foo: 123 }; obj [1, 2]; obj (a:number) a 1; obj true; // 报错 obj hi; // 报错 obj 1; // 报错 大多数时候我们使用对象类型只希望包含真正的对象不希望包含原始类型。所以建议总是使用小写类型object不使用大写类型Object 注意无论是大写的Object类型还是小写的object类型都只包含 JavaScript 内置对象原生的属性和方法用户自定义的属性和方法都不存在于这两个类型之中 4.undefined和null的特殊性 undefined和null既是值又是类型 作为值它们有一个特殊的地方任何其他类型的变量都可以赋值为undefined或null。 JavaScript 的行为是变量如果等于undefined就表示还没有赋值如果等于null就表示值为空。所以TypeScript 就允许了任何类型的变量都可以赋值为这两个值。 let age:number 24;age null; // 报错 age undefined; // 报错 上面示例中打开–strictNullChecks以后number类型的变量age就不能赋值为undefined和null。 这个选项在配置文件tsconfig.json的写法如下。 {compilerOptions: {strictNullChecks: true// …} } 打开strictNullChecks以后undefined和null这两种值也不能互相赋值了。undefined和null只能赋值给自身或者any类型和unknown类型的变量 let x:any undefined; let y:unknown null; 5.联合类型 联合类型union types指的是多个类型组成的一个新类型使用符号|表示。 联合类型A|B表示任何一个类型只要属于A或B就属于联合类型A|B。 let x:string|number;x 123; // 正确 x abc; // 正确 6.交叉类型 交叉类型intersection types指的多个类型组成的一个新类型使用符号表示 交叉类型的主要用途是表示对象的合成: let obj:{ foo: string } { bar: string };obj {foo: hello,bar: world }; 上面示例中变量obj同时具有属性foo和属性bar。 交叉类型常常用来为对象类型添加新属性。 type A { foo: number };type B A { bar: number }; 上面示例中类型B是一个交叉类型用来在A的基础上增加了属性bar。 7.type命令 type命令用来定义一个类型的别名。 type Age number;let age:Age 55; 上面示例中type命令为number类型定义了一个别名Age。这样就能像使用number一样使用Age作为类型。 8.typeof运算符 TypeScript 将typeof运算符移植到了类型运算它的操作数依然是一个值但是返回的不是字符串而是该值的 TypeScript 类型。 const a { x: 0 };type T0 typeof a; // { x: number } type T1 typeof a.x; // number 上面示例中typeof a表示返回变量a的 TypeScript 类型{ x: number }。同理typeof a.x返回的是属性x的类型number。 这种用法的typeof返回的是 TypeScript 类型所以只能用在类型运算之中即跟类型相关的代码之中不能用在值运算 type T typeof Date(); // 报错上面示例会报错原因是 typeof 的参数不能是一个值的运算式而Date()需要运算才知道结果。 另外typeof命令的参数不能是类型。 type Age number; type MyAge typeof Age; // 报错 上面示例中Age是一个类型别名用作typeof命令的参数就会报错。 typeof 是一个很重要的 TypeScript 运算符有些场合不知道某个变量foo的类型这时使用typeof foo就可以获得它的类型。 9.块级类型声明 TypeScript 支持块级类型声明即类型可以声明在代码块用大括号表示里面并且只在当前代码块有效 if (true) {type T number;let v:T 5; } else {type T string;let v:T hello; } 10.类型的兼容 type T number|string;let a:number 1; let b:T a; 变量a和b的类型是不一样的但是变量a赋值给变量b并不会报错。这时我们就认为b的类型兼容a的类型。 如果类型A的值可以赋值给类型B那么类型A就称为类型B的子类型subtype。在上例中类型number就是类型number|string的子类型 let a:hi hi; let b:string hello;b a; // 正确 a b; // 报错 上面示例中hi是string的子类型string是hi的父类型。所以变量a可以赋值给变量b但是反过来就会报错。 子类型继承了父类型的所有特征所以可以用在父类型的场合。但是子类型还可能有一些父类型没有的特征所以父类型不能用在子类型的场合。 3.数组 TypeScript 数组有一个根本特征所有成员的类型必须相同但是成员数量是不确定的可以是无限数量的成员也可以是零成员 声明的方式   let arr:number[] [1, 2, 3]; let arr:(number|string)[]; let arr:Arraynumber [1, 2, 3]; 数组类型声明了以后成员数量是不限制的任意数量的成员都可以也可以是空数组。 let arr:number[]; arr []; arr [1]; arr [1, 2]; arr [1, 2, 3];let arr1:number[] [1, 2, 3];arr1[3] 4; arr1.length 2;arr1// [1, 2] 上面示例中数组arr无论有多少个成员都是正确的。 这种规定的隐藏含义就是数组的成员是可以动态变化的数组增加成员或减少成员都是可以的。 1.类型推断 如果数组变量没有声明类型TypeScript 就会推断数组成员的类型。这时推断行为会因为值的不同而有所不同。后面为这个数组赋值时TypeScript 会自动更新类型推断。 const arr []; arr // 推断为 any[]arr.push(123); arr // 推断类型为 number[]arr.push(abc); arr // 推断类型为 (string|number)[] 但是类型推断的自动更新只发生初始值为空数组的情况。如果初始值不是空数组类型推断就不会更新。 // 推断类型为 number[] const arr [123];arr.push(abc); // 报错 上面示例中数组变量arr的初始值是[123]TypeScript 就推断成员类型为number。新成员如果不是这个类型TypeScript 就会报错而不会更新类型推断。 2.只读数组const断言 TypeScript 允许声明只读数组方法是在数组类型前面加上readonly关键字。arr是一个只读数组删除、修改、新增数组成员都会报错。 const arr:readonly number[] [0, 1];arr[1] 2; // 报错 arr.push(3); // 报错 delete arr[0]; // 报错 TypeScript 将readonly number[]与number[]视为两种不一样的类型后者是前者的子类型。 所以子类型number[]可以用于所有使用父类型的场合反过来就不行。 let a1:number[] [0, 1]; let a2:readonly number[] a1; // 正确a1 a2; // 报错 只读数组还有一种声明方法就是使用“const 断言”。   const arr [0, 1] as const;arr[0] [2]; // 报错
上面示例中as const告诉 TypeScript推断类型时要把变量arr推断为只读数组从而使得数组成员无法改变。 3.多维数组 var multi:number[][] [[1,2,3], [23,24,25]]; 4.元组 它表示成员类型可以自由设置的数组即数组的各个成员的类型可以不同 数组的成员类型写在方括号外面number[]元组的成员类型是写在方括号里面[number] // 数组 let a:number[] [1];// 元组 let t:[number] [1]; 使用元组时必须明确给出类型声明上例的[number]不能省略否则 TypeScript 会把一个值自动推断为数组。 // a 的类型被推断为 (number | boolean)[] let a [1, true]; 元组成员的类型可以添加问号后缀?表示该成员是可选的。所有可选成员必须在必选成员之后。 let a:[number, number?] [1]; 由于需要声明每个成员的类型所以大多数情况下元组的成员数量是有限的从类型声明就可以明确知道元组包含多少个成员越界的成员会报错。 let x:[string, string] [a, b];x[2] c; // 报错 但是使用扩展运算符…可以表示不限成员数量的元组。 type NamedNums [string,…number[] ];const a:NamedNums [A, 1, 2]; const b:NamedNums [B, 1, 2, 3]; 1.只读元组 元组也可以是只读的不允许修改有两种写法。 // 写法一 type t readonly [number, string]// 写法二 type t Readonly[number, string] 跟数组一样只读元组是元组的父类型。所以元组可以替代只读元组而只读元组不能替代元组。 type t1 readonly [number, number]; type t2 [number, number];let x:t2 [1, 2]; let y:t1 x; // 正确x y; // 报错 2、成员数量推断 function f(point: [number, number]) {if (point.length 3) { // 报错// …} } 上面示例会报错原因是 TypeScript 发现元组point的长度是2不可能等于3这个判断无意义 如果包含了可选成员TypeScript 会推断出可能的成员数量。 function f(point:[number, number?, number?] ) {if (point.length 4) { // 报错// …} } 如果使用了扩展运算符TypeScript 就无法推断出成员数量。 const myTuple:[…string[]] [a, b, c];if (myTuple.length 4) { // 正确// … } 一旦扩展运算符使得元组的成员数量无法推断TypeScript 内部就会把该元组当成数组处理 3.扩展运算符与成员数量 如果函数调用时使用扩展运算符传入函数参数可能发生参数数量与数组长度不匹配的报错。 const arr [1, 2];function add(x:number, y:number){// … }add(…arr) // 报错 解决这个问题的一个方法就是把成员数量不确定的数组写成成员数量确定的元组再使用扩展运算符。 const arr:[number, number] [1, 2];function add(x:number, y:number){// … }add(…arr) // 正确 5.symbol类型 Symbol 值通过Symbol()函数生成。在 TypeScript 里面Symbol 的类型使用symbol表示。 let x:symbol Symbol(); let y:symbol Symbol();x y // false 上面示例中变量x和y的类型都是symbol且都用Symbol()生成但是它们是不相等的。 1.unique symbol symbol的一个子类型unique symbol它表示单个的、某个具体的 Symbol 值。 因为unique symbol表示单个值所以这个类型的变量是不能修改值的只能用const命令声明不能用let声明。 // 正确 const x:unique symbol Symbol();// 报错 let y:unique symbol Symbol();const x:unique symbol Symbol(); // 等同于 const x Symbol(); 每个声明为unique symbol类型的变量它们的值都是不一样的其实属于两个值类型。 const a:unique symbol Symbol(); const b:unique symbol Symbol();a b // 报错 变量a和b是两个类型就不能把一个赋值给另一个。 const a:unique symbol Symbol(); const b:unique symbol a; // 报错 如果要写成与变量a同一个unique symbol值类型只能写成类型为typeof a。 const a:unique symbol Symbol(); const b:typeof a a; // 正确 unique symbol 类型是 symbol 类型的子类型所以可以将前者赋值给后者但是反过来就不行。 const a:unique symbol Symbol();const b:symbol a; // 正确const c:unique symbol b; // 报错 2.类型推断 let命令声明的变量推断类型为 symbol。 // 类型为 symbol let x Symbol(); const命令声明的变量推断类型为 unique symbol。 // 类型为 unique symbol const x Symbol(); 但是const命令声明的变量如果赋值为另一个 symbol 类型的变量则推断类型为 symbol。 let x Symbol();// 类型为 symbol const y x; let命令声明的变量如果赋值为另一个 unique symbol 类型的变量则推断类型还是 symbol。 const x Symbol();// 类型为 symbol let y x; 6.函数类型 数的类型声明需要在声明函数时给出参数的类型和返回值的类型。 function hello(txt:string ):void {console.log(hello txt); } 上面示例中函数hello()在声明时需要给出参数txt的类型string以及返回值的类型void后者写在参数列表的圆括号后面。void类型表示没有返回值返回值的类型通常可以不写因为 TypeScript 自己会推断出来。 如果变量被赋值为一个函数变量的类型有两种写法。 // 写法一 const hello function (txt:string) {console.log(hello txt); }// 写法二 const hello:(txt:string) voidfunction (txt) {console.log(hello txt); }; 如果函数的类型定义很冗长或者多个函数使用同一种类型写法二用起来就很麻烦。因此往往用type命令为函数类型定义一个别名便于指定给其他变量。 type MyFunc (txt:string) void;const hello:MyFunc function (txt) {console.log(hello txt); }; 函数的实际参数个数可以少于类型指定的参数个数但是不能多于即 TypeScript 允许省略参数。 let myFunc:(a:number, b:number) number;myFunc (a:number) a; // 正确myFunc (a:number, b:number, c:number ) a b c; // 报错 如果一个变量要套用另一个函数类型有一个小技巧就是使用typeof运算符。 function add(x:number,y:number ) {return x y; }const myAdd:typeof add function (x, y) {return x y; } 1.Function类型 TypeScript 提供 Function 类型表示函数任何函数都属于这个类型。不建议这样使用不然传入和传出的都是any类型 function doSomething(f:Function) {return f(1, 2, 3); } 2.箭头函数 箭头函数是普通函数的一种简化写法它的类型写法与普通函数类似。 const repeat (str:string,times:number ):string str.repeat(times); 看一个例子。 type Person { name: string };const people [alice, bob, jan].map((name):Person ({name}) ); 上面示例中Person是一个类型别名代表一个对象该对象有属性name。变量people是数组的map()方法的返回值。 map()方法的参数是一个箭头函数(name):Person ({name})该箭头函数的参数name的类型省略了因为可以从map()的类型定义推断出来箭头函数的返回值类型为Person。相应地变量people的类型是Person[]。 至于箭头后面的({name})表示返回一个对象该对象有一个属性name它的属性值为变量name的值。这里的圆括号是必须的否则(name):Person {name}的大括号表示函数体即函数体内有一行语句name同时由于没有return语句这个函数不会返回任何值。 3.可选参数 如果函数的某个参数可以省略则在参数名后面加问号表示。参数名带有问号表示该参数的类型实际上是原始类型|undefined它有可能为undefined function f(x?:number) {// … }f(); // OK f(10); // OK 函数体内部用到可选参数时需要判断该参数是否为undefined。 let myFunc:(a:number, b?:number) number; myFunc function (x, y) {if (y undefined) {return x;}return x y; } 上面示例中由于函数的第二个参数为可选参数所以函数体内部需要判断一下该参数是否为空。 4.参数默认值 设置了默认值的参数就是可选的。如果不传入该参数它就会等于默认值。可选参数与默认值不能同时使用。 function createPoint(x:number 0,y:number 0 ):[number, number] {return [x, y]; }createPoint() // [0, 0] 具有默认值的参数如果不位于参数列表的末尾调用时不能省略如果要触发默认值必须显式传入undefined。 function add(x:number 0,y:number ) {return x y; }add(1) // 报错 add(undefined, 1) // 正确 5.参数解构 函数参数如果存在变量解构类型写法如下。 function f([x, y]: [number, number] ) {// … }function sum({ a, b, c }: {a: number;b: number;c: number} ) {console.log(a b c); } 参数解构可以结合类型别名type 命令一起使用代码会看起来简洁一些。 type ABC { a:number; b:number; c:number };function sum({ a, b, c }:ABC) {console.log(a b c); } 6.rest参数 rest 参数表示函数剩余的所有参数它可以是数组剩余参数类型相同也可能是元组剩余参数类型不同。 // rest 参数为数组 function joinNumbers(…nums:number[]) {// … }// rest 参数为元组 function f(…args:[boolean, number]) {// … } 下面是一个 rest 参数的例子。 function multiply(n:number, …m:number[]) {return m.map((x) n * x); } 上面示例中参数m就是 rest 类型它的类型是一个数组。 rest 参数甚至可以嵌套。 function f(…args:[boolean, …string[]]) {// … } rest 参数可以与变量解构结合使用。 function repeat(…[str, times]: [string, number] ):string {return str.repeat(times); }// 等同于 function repeat(str: string,times: number ):string {return str.repeat(times); } 7.readonly只读参数 如果函数内部不能修改某个参数可以在函数定义时在参数类型前面加上readonly关键字表示这是只读参数。 function arraySum(arr:readonly number[] ) {// …arr[0] 0; // 报错 } 8.void类型 void 类型表示函数没有返回值。如果返回其他值就会报错但是允许返回null和undefined如果打开了strictNullChecks编译选项那么 void 类型只允许返回undefined。如果返回null就会报错。这是因为 JavaScript 规定如果函数没有返回值就等同于返回undefined。 function f():void {console.log(hello); } 需要特别注意的是如果变量、对象方法、函数参数的类型是 void 类型的函数那么并不代表不能赋值为有返回值的函数。恰恰相反该变量、对象方法和函数参数可以接受返回任意值的函数这时并不会报错。 type voidFunc () void;const f:voidFunc () {return 123; }; 这是因为这时 TypeScript 认为这里的 void 类型只是表示该函数的返回值没有利用价值或者说不应该使用该函数的返回值。只要不用到这里的返回值就不会报错。 const src [1, 2, 3]; const ret [];src.forEach(el ret.push(el)); 上面示例中push()有返回值表示新插入的元素在数组里面的位置。但是对于forEach()方法来说这个返回值是没有作用的根本用不到所以 TypeScript 不会报错。 9.never类型 never类型表示肯定不会出现的值。它用在函数的返回值就表示某个函数肯定不会返回值即函数不会正常执行结束。 它主要有以下两种情况。 1抛出错误的函数。 function fail(msg:string):never {throw new Error(msg); } 上面示例中函数fail()会抛错不会正常退出所以返回值类型是never。 注意只有抛出错误才是 never 类型。如果显式用return语句返回一个 Error 对象返回值就不是 never 类型。 function fail():Error {return new Error(Something failed); } 上面示例中函数fail()返回一个 Error 对象所以返回值类型是 Error。 2无限执行的函数。 const sing function():never {while (true) {console.log(sing);} }; 注意never类型不同于void类型。前者表示函数没有执行结束不可能有返回值后者表示函数正常执行结束但是不返回值或者说返回undefined。 如果程序中调用了一个返回值类型为never的函数那么就意味着程序会在该函数的调用位置终止永远不会继续执行后续的代码。 10.局部类型 函数内部允许声明其他类型该类型只在函数内部有效称为局部类型。 function hello(txt:string) {type message string;let newTxt:message hello txt;return newTxt; }const newTxt:message hello(world); // 报错 上面示例中类型message是在函数hello()内部定义的只能在函数内部使用。在函数外部使用就会报错。 11.函数重载 有些函数可以接受不同类型或不同个数的参数并且根据参数的不同会有不同的函数行为。这种根据参数类型不同执行不同逻辑的行为称为函数重载function overload。 reverse(abc) // cba reverse([1, 2, 3]) // [3, 2, 1] 分别对函数reverse()的两种参数情况给予了类型声明。但是到这里还没有结束后面还必须对函数reverse()给予完整的类型声明。 function reverse(str:string):string; function reverse(arr:any[]):any[]; function reverse(stringOrArray:string|any[] ):string|any[] {if (typeof stringOrArray string)return stringOrArray.split().reverse().join();elsereturn stringOrArray.slice().reverse(); } 函数体内部需要判断参数的类型及个数并根据判断结果执行不同的操作。 function add(x:number,y:number ):number; function add(x:any[],y:any[] ):any[]; function add(x:number|any[],y:number|any[] ):number|any[] {if (typeof x number typeof y number) {return x y;} else if (Array.isArray(x) Array.isArray(y)) {return […x, …y];}throw new Error(wrong parameters); } 重载声明的排序很重要因为 TypeScript 是按照顺序进行检查的一旦发现符合某个类型声明就不再往下检查了所以类型最宽的声明应该放在最后面防止覆盖其他类型声明。 function f(x:any):number; function f(x:string): 0|1; function f(x:any):any {// … }const a:0|1 f(hi); // 报错 上面声明中第一行类型声明x:any范围最宽导致函数f()的调用都会匹配这行声明无法匹配第二行类型声明所以最后一行调用就报错了因为等号两侧类型不匹配左侧类型是0|1右侧类型是number。这个函数重载的正确顺序是第二行类型声明放到第一行的位置 7.对象 1.可选属性 如果某个属性是可选的即可以忽略需要在属性名后面加一个问号。可选属性等同于允许赋值为undefined const obj: {x: number;y?: number; } { x: 1 }; / 写法一 let firstName (user.firstName undefined)? Foo : user.firstName; let lastName (user.lastName undefined)? Bar : user.lastName;// 写法二 let firstName user.firstName ?? Foo; let lastName user.lastName ?? Bar; 上面示例中写法一使用三元运算符?:判断是否为undefined并设置默认值。写法二使用 Null 判断运算符??与写法一的作用完全相同。 TypeScript 提供编译设置ExactOptionalPropertyTypes只要同时打开这个设置和strictNullChecks可选属性就不能设为undefined // 打开 ExactOptionsPropertyTypes 和 strictNullChecks const obj: {x: number;y?: number; } { x: 1, y: undefined }; // 报错 上面示例中打开了这两个设置以后可选属性就不能设为undefined了。 注意可选属性与允许设为undefined的必选属性是不等价的。 type A { x:number, y?:number }; type B { x:number, y:number|undefined };const ObjA:A { x: 1 }; // 正确 const ObjB:B { x: 1 }; // 报错 上面示例中属性y如果是一个可选属性那就可以省略不写如果是允许设为undefined的必选属性一旦省略就会报错必须显式写成{ x: 1, y: undefined } 2.只读属性 属性名前面加上readonly关键字表示这个属性是只读属性不能修改。 interface MyInterface {readonly prop: number; } 注意如果属性值是一个对象readonly修饰符并不禁止修改该对象的属性只是禁止完全替换掉该对象。 interface Home {readonly resident: {name: string;age: number}; }const h:Home {resident: {name: Vicky,age: 42} };h.resident.age 32; // 正确 h.resident {name: Kate,age: 23 } // 报错 如果希望属性值是只读的除了声明时加上readonly关键字还有一种方法就是在赋值时在对象后面加上只读断言a const myUser {name: Sabrina, } as const;myUser.name Cynthia; // 报错 注意上面的as const属于 TypeScript 的类型推断如果变量明确地声明了类型那么 TypeScript 会以声明的类型为准。 const myUser:{ name: string } {name: Sabrina, } as const;myUser.name Cynthia; // 正确 3.属性名的索引类型  type MyObj {[property: string]: string };const obj:MyObj {foo: a,bar: b,baz: c, }; 上面示例中类型MyObj的属性名类型就采用了表达式形式写在方括号里面。[property: string]的property表示属性名这个是可以随便起的它的类型是string即属性名类型为string。也就是说不管这个对象有多少属性只要属性名为字符串且属性值也是字符串就符合这个类型声明。 4.解构赋值 解构赋值用于直接从对象中提取属性。 const {id, name, price} product;上面语句从对象product提取了三个属性并声明属性名的同名变量。 5.结构类型原则 只要对象 B 满足 对象 A 的结构特征TypeScript 就认为对象 B 兼容对象 A 的类型这称为“结构类型”原则structural typing。 type A {x: number; };type B {x: number;y: number; }; 对象A只有一个属性x类型为number。对象B满足这个特征因此兼容对象A只要可以使用A的地方就可以使用B。 const B {x: 1,y: 1 };const A:{ x: number } B; // 正确 8.interface 1.interface对象的5种语法 对象属性对象的属性索引对象方法函数构造函数 1对象属性 interface Point {x: number;y: number; } 上面示例中x和y都是对象的属性分别使用冒号指定每个属性的类型。 2对象的属性索引 interface A {[prop: string]: number; } 上面示例中[prop: string]就是属性的字符串索引表示属性名只要是字符串都符合类型要求。 3对象的方法 对象的方法共有三种写法。 // 写法一 interface A {f(x: boolean): string; }// 写法二 interface B {f: (x: boolean) string; }// 写法三 interface C {f: { (x: boole 4函数 interface 也可以用来声明独立的函数。 interface Add {(x:number, y:number): number; }const myAdd:Add (x,y) x y; 上面示例中接口Add声明了一个函数类型。 5构造函数 interface 内部可以使用new关键字表示构造函数。 interface ErrorConstructor {new (message?: string): Error; } 2.interface的继承 2.1.interface继承interface interface 可以使用extends关键字继承其他 interface。 interface Shape {name: string; }interface Circle extends Shape {radius: number; } 上面示例中Circle继承了Shape所以Circle其实有两个属性name和radius。这时Circle是子接口Shape是父接口。 extends关键字会从继承的接口里面拷贝属性类型这样就不必书写重复的属性。 interface 允许多重继承。 interface Style {color: string; }interface Shape {name: string; }interface Circle extends Style, Shape {radius: number; } 上面示例中Circle同时继承了Style和Shape所以拥有三个属性color、name和radius。 多重接口继承实际上相当于多个父接口的合并 2.2interface继承type interface 可以继承type命令定义的对象类型。i type Country {name: string;capital: string; }interface CountryWithPop extends Country {population: number; } 上面示例中CountryWithPop继承了type命令定义的Country对象并且新增了一个population属性。 注意如果type命令定义的类型不是对象interface 就无法继承。 2.3interface继承class interface 还可以继承 class即继承该类的所有成员。 class A {x:string ;y():boolean {return true;} }interface B extends A {z: number } 上面示例中B继承了A因此B就具有属性x、y()和z。 实现B接口的对象就需要实现这些属性。 const b:B {x: ,y: function(){ return true },z: 123 } 上面示例中对象b就实现了接口B而接口B又继承了类A。 3.接口合并 多个同名接口会合并成一个接口。 interface Box {height: number;width: number; }interface Box {length: number; } 上面示例中两个Box接口会合并成一个接口同时有height、width和length三个属性。 4.interface与type的异同 interface命令与type命令作用类似都可以表示对象类型。 很多对象类型既可以用 interface 表示也可以用 type 表示。而且两者往往可以换用几乎所有的 interface 命令都可以改写为 type 命令。 它们的相似之处首先表现在都能为对象类型起名。 type Country {name: string;capital: string; }interface Coutry {name: string;capital: string; } 上面示例是type命令和interface命令分别定义同一个类型。 4.1interface与type的区别 1type能够表示非对象类型而interface只能表示对象类型包括数组、函数等。 2interface可以继承其他类型type不支持继承。 继承的主要作用是添加属性type定义的对象类型如果想要添加属性只能使用运算符重新定义一个类型。 type Animal {name: string }type Bear Animal {honey: boolean } 上面示例中类型Bear在Animal的基础上添加了一个属性honey。上例的运算符表示同时具备两个类型的特征因此可以起到两个对象类型合并的作用。 作为比较interface添加属性采用的是继承的写法。 interface Animal {name: string }interface Bear extends Animal {honey: boolean } 继承时type 和 interface 是可以换用的。interface 可以继承 type。 type Foo { x: number; };interface Bar extends Foo {y: number; } type 也可以继承 interface。 interface Foo {x: number; }type Bar Foo { y: number; }; 3同名interface会自动合并同名type则会报错。也就是说TypeScript 不允许使用type多次定义同一个类型。 type A { foo:number }; // 报错 type A { bar:number }; // 报错 上面示例中type两次定义了类型A导致两行都会报错。 4interface不能包含属性映射mappingtype可以 interface Point {x: number;y: number; }// 正确 type PointCopy1 {[Key in keyof Point]: Point[Key]; };// 报错 interface PointCopy2 {[Key in keyof Point]: Point[Key]; }; 5this关键字只能用于interface。 // 正确 interface Foo {add(num:number): this; };// 报错 type Foo {add(num:number): this; }; 6type 可以扩展原始数据类型interface 不行。 // 正确 type MyStr string {type: new };// 报错 interface MyStr extends string {type: new } 7interface无法表达某些复杂类型比如交叉类型和联合类型但是type可以。 type A { /* … / }; type B { / … */ };type AorB A | B; type AorBwithName AorB {name: string }; 上面示例中类型AorB是一个联合类型AorBwithName则是为AorB添加一个属性。这两种运算interface都没法表达。 综上所述如果有复杂的类型运算那么没有其他选择只能使用type一般情况下interface灵活性比较高便于扩充类型或自动合并建议优先使用