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

苏州吴中区做网站的淘客推广

苏州吴中区做网站的,淘客推广,网站正能量下载直接进入主页可以吗安全吗,网站开发实例1. any 类型,unknown 类型,never 类型 TypeScript 有两个“顶层类型”(any和unknown),但是“底层类型”只有never唯一一个 1、any 1.1 基本含义 any 类型表示没有任何限制,该类型的变量可以赋予任意类型的…

1. any 类型,unknown 类型,never 类型


 TypeScript 有两个“顶层类型”(any和unknown),但是“底层类型”只有never唯一一个

1、any


1.1 基本含义


any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值。变量类型一旦设为any,TypeScript 实际上会关闭这个变量的类型检查。即使有明显的类型错误,只要句法正确,都不会报错。

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;

除了undefinednull这两个值不能转为对象,其他任何值都可以赋值给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就不能赋值为undefinednull

这个选项在配置文件tsconfig.json的写法如下。

{"compilerOptions": {"strictNullChecks": true// ...}
}

 打开strictNullChecks以后,undefinednull这两种值也不能互相赋值了。undefinednull只能赋值给自身,或者any类型和unknown类型的变量

let x:any     = undefined;
let y:unknown = null;

5.联合类型

联合类型(union types)指的是多个类型组成的一个新类型,使用符号|表示。

联合类型A|B表示,任何一个类型只要属于AB,就属于联合类型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;

变量ab的类型是不一样的,但是变量a赋值给变量b并不会报错。这时,我们就认为,b的类型兼容a的类型。

如果类型A的值可以赋值给类型B,那么类型A就称为类型B的子类型(subtype)。在上例中,类型number就是类型number|string的子类型

let a:'hi' = 'hi';
let b:string = 'hello';b = a; // 正确
a = b; // 报错

上面示例中,histring的子类型,stringhi的父类型。所以,变量a可以赋值给变量b,但是反过来就会报错。

子类型继承了父类型的所有特征,所以可以用在父类型的场合。但是,子类型还可能有一些父类型没有的特征,所以父类型不能用在子类型的场合。

3.数组

TypeScript 数组有一个根本特征:所有成员的类型必须相同,但是成员数量是不确定的,可以是无限数量的成员,也可以是零成员

声明的方式:
 

let arr:number[] = [1, 2, 3];
let arr:(number|string)[];

let arr:Array<number> = [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

上面示例中,变量xy的类型都是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 // 报错

变量ab是两个类型,就不能把一个赋值给另一个。

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) => void
= function (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;
}

上面示例中,xy都是对象的属性,分别使用冒号指定每个属性的类型。

(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其实有两个属性nameradius。这时,Circle是子接口,Shape是父接口。

extends关键字会从继承的接口里面拷贝属性类型,这样就不必书写重复的属性。

interface 允许多重继承。

interface Style {color: string;
}interface Shape {name: string;
}interface Circle extends Style, Shape {radius: number;
}

上面示例中,Circle同时继承了StyleShape,所以拥有三个属性colornameradius

多重接口继承,实际上相当于多个父接口的合并

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就具有属性xy()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的区别:


(1)type能够表示非对象类型,而interface只能表示对象类型(包括数组、函数等)。

(2)interface可以继承其他类型,type不支持继承。

继承的主要作用是添加属性,type定义的对象类型如果想要添加属性,只能使用&运算符,重新定义一个类型。

type Animal = {name: string
}type Bear = Animal & {honey: boolean
}

 

上面示例中,类型BearAnimal的基础上添加了一个属性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,导致两行都会报错。

(4)interface不能包含属性映射(mapping),type可以

interface Point {x: number;y: number;
}// 正确
type PointCopy1 = {[Key in keyof Point]: Point[Key];
};// 报错
interface PointCopy2 {[Key in keyof Point]: Point[Key];
};

(5)this关键字只能用于interface

// 正确
interface Foo {add(num:number): this;
};// 报错
type Foo = {add(num:number): this;
};

6)type 可以扩展原始数据类型,interface 不行。

// 正确
type MyStr = string & {type: 'new'
};// 报错
interface MyStr extends string {type: 'new'
}

(7)interface无法表达某些复杂类型(比如交叉类型和联合类型),但是type可以。

type A = { /* ... */ };
type B = { /* ... */ };type AorB = A | B;
type AorBwithName = AorB & {name: string
};

上面示例中,类型AorB是一个联合类型,AorBwithName则是为AorB添加一个属性。这两种运算,interface都没法表达。

综上所述,如果有复杂的类型运算,那么没有其他选择只能使用type;一般情况下,interface灵活性比较高,便于扩充类型或自动合并,建议优先使用

http://www.ritt.cn/news/2523.html

相关文章:

  • 网站建设自建服务器手机百度网址大全首页
  • 网站排行榜上升代码足球世界排名国家最新
  • 上海市住房和城乡建设管理委员会门户网站佛山抖音seo
  • 南京 网站建设百度科技有限公司
  • 怎样帮人做网站挣钱产品推广图片
  • 常熟市住房和城乡建设部网站淘宝seo搜索引擎优化
  • 网站设计公司成都怎么查权重查询
  • 网站空间空间app下载推广
  • 招聘网站策划书做企业网站建设的公司
  • 怎样做企业网站上海百度搜索排名优化
  • 海口做网站的公司游戏推广论坛
  • 深圳论坛网站设计哪家公司好免费发布网站seo外链
  • 自助设计网站广告咨询
  • 网站关于我们模板网上营销是做什么的
  • 西安有哪些做网站建设的公司建设网站制作
  • 湾里南昌网站建设公司十大洗脑广告
  • 网站被主流搜索引擎收录的网页数量是多少文案代写平台
  • 洛阳做多屏合一网站网络小说网站三巨头
  • 毕业设计做网站怎样的工作量算达标网络安全培训机构排名
  • 千博政府网站管理系统西安网站建设公司电话
  • wordpress地址如何修改seo和sem分别是什么
  • 陕西科技网站建设营销型网站建设的步骤流程是什么
  • 怎么使用wordpress建站企业如何网络推广
  • 桂林做网站的公司有哪些阿里巴巴国际站官网
  • 网站建站网站设计公司百度关键词指数排行
  • 网站建设可用性会计培训班哪个机构比较好
  • 甘肃三轮建设监理网站跨境电商平台
  • 门店门面设计效果图江西优化中心
  • 怎样找公司做单的网站seo智能优化软件
  • 上海市网站建设公司58百度一下免费下载安装