命名空间 namespace

一般在一个文件里,重复声明变量是不允许的,也会报错。

  1. let x = 1;
  2. let x = 2; //报错:无法重新声明块范围变量“x”
  3. class Person{}
  4. class Person{} //报错

我们可以用命名空间来解决,这样各自就不会冲突了。

  1. namespace a{
  2. let x = 1;
  3. class Person{}
  4. }
  5. namespace b{
  6. let x = 2;
  7. class Person{}
  8. }
  9. //编译为
  10. var a;
  11. (function (a) {
  12. var x = 1;
  13. })(a || (a = {}));
  14. var b;
  15. (function (b) {
  16. var x = 2;
  17. })(b || (b = {}));

数据类型

布尔、数字、字符串类型

  1. let p1:boolean = true;
  2. let p2:(boolean) = false; //括号加不加都行
  3. let n1:number = 1;
  4. let s1:string = 'jack';
  5. //编译为
  6. var p1 = true;
  7. var p2 = false; //括号加不加都行
  8. var n1 = 1;
  9. var s1 = 'jack';

number 和 Number 的区别
  • number 是基础类型
  • Number 是number的封装类,是包装器对象类型
    1. let n1:number = 1;
    2. let n2:Number = 1; //用来描述实例的,类也可以当做类型
    3. let n3:number = Number(1); //Number(1) 还是一个数字类型,number
    4. let n4:Number = new Number(1); //new Number(1)就是一个对象类型,只能用 大Number 类型

数组类型(array)

  1. let p1:number[] = [1,2,3];
  2. let p2:string[] = ['a', 'b', 'c'];
  3. let p3:(number|string)[] = [1, 'b', 3];
  4. //泛型方式来声明
  5. let p4:Array<number> = [1,2,3];
  6. let p5:Array<number | string> = [1, 'b', 3];
  7. //编译为
  8. var p1 = [1, 2, 3];

元祖类型(tuple)

  • 在typesccript的基础类型中,元祖(tuple)表示一个已知 数量类型 的数组。
  • 元祖是数组的子类型 ```typescript //都会报错。初始化创建的时候只能按要求填入数据,即 [字符串, 数字, 布尔值] // let t1:[string, number, boolean] = [‘jack’, 1]; // let t1:[string, number, boolean] = [‘jack’, 1, ‘cy’]; // let t1:[string, number, boolean] = [‘jack’, 1, true, 2];

let t1:[string, number, boolean] = [‘jack’, 1, true];

console.log(t1); //[“jack”, 1, true] console.log(t1.length); //3 console.log(t1[0].length); //4 console.log(t1[1].toFixed(2)); //1.00

// 向元祖中添加数据,只能添加元祖中定义的类型 t1.push(2); t1.push(‘cy’); console.log(t1); //[“jack”, 1, true, 2, “cy”]

//编译为 var t1 = [‘jack’, 1, true];

  1. <a name="qIrUP"></a>
  2. ##### 元祖与数组的区别
  3. | **元祖** | **数组** |
  4. | --- | --- |
  5. | 每一项可以是不同的类型 | 每一项都是同一种类型 |
  6. | 有预定义的长度 | 没有长度限制 |
  7. | 用于表示一个固定的结构 | 用于表示一个列表 |
  8. <a name="c8e1e31f"></a>
  9. ## 对象类型(object)
  10. ```typescript
  11. let a1:object = 1; //error
  12. let a2:object = { name: 'jack' }; //ok
  13. interface Obj {
  14. [key:string]: any;
  15. };
  16. let b1:Obj = 1; //error
  17. let b2:object = { name: 'jack' }; //ok

函数类型(Function)

注意首字母是大写

  1. let fn = function(){};
  2. let a1:Function = 1; //error
  3. let a2:Function = fn; //ok
  4. type Fn = (...args: any) => any;
  5. let b1:Function = 1; //error
  6. let b2:Function = fn; //ok

枚举类型

事先考虑某一个变量的所有的可能的值,尽量用自然语言中的单词表示它的每一个值。
比如性别、月份、星期、颜色、单位、学历

普通枚举

  1. enum Gender {
  2. GIRL, // 默认从0开始
  3. BOY,
  4. }
  5. console.log(Gender);
  6. // 从结果来看: 可以枚举,也可以反举
  7. // {
  8. // 0: "GIRL",
  9. // 1: "BOY",
  10. // GIRL: 0,
  11. // BOY: 1,
  12. // }
  13. // 编译为
  14. var Gender;
  15. (function (Gender) {
  16. Gender[Gender["GIRL"] = 0] = "GIRL"; /*先把0赋给Gender["GIRL"], 再把"GIRL"赋给Gender[0]*/
  17. Gender[Gender["BOY"] = 1] = "BOY";
  18. })(Gender || (Gender = {}));

异构枚举

  1. enum Animal {
  2. BIRD = 'bird',
  3. DOG = 1,
  4. CAT,
  5. }
  6. console.log(Animal);
  7. // {
  8. // 1: "DOG"
  9. // 2: "CAT"
  10. // BIRD: "bird"
  11. // CAT: 2
  12. // DOG: 1
  13. // }
  14. // 编译为
  15. var Animal;
  16. (function (Animal) {
  17. Animal["BIRD"] = "bird";
  18. Animal[Animal["DOG"] = 1] = "DOG";
  19. Animal[Animal["CAT"] = 2] = "CAT";
  20. })(Animal || (Animal = {}));

常量枚举

常量枚举与普通枚举的区别是,它会在编译阶段被删除,并且不能包含计算成员。
假如包含了计算成员,则会在编译阶段报错。

  1. //注意,前边加了一个 const。加上后,就不会生成对象了,但是也不支持反举了
  2. const enum Colors {
  3. RED,
  4. YELLOW,
  5. BLUE,
  6. }
  7. let myColor = [Colors.RED, Colors.YELLOW, Colors.BLUE];
  8. console.log(myColor); //[ 0, 1, 2 ]
  9. // 编译后
  10. var myColor = [0 /* RED */, 1 /* YELLOW */, 2 /* BLUE */];
  11. console.log(myColor); //[ 0, 1, 2 ]

任意类型(any)

  • any 就是可以赋给任意类型,不进行类型检测。
  • 第三方库没有提供类型文件时可以使用 any
  • 类型转换遇到困难时
  • 数据结构太复杂难以定义 ```typescript // 如果变量定义为any类型,就跟jS差不多,不进行类型检查 let root:any = document.getElementById(‘root’); root.style.color = ‘red’;

//编译为 var root = document.getElementById(‘root’); root.style.color = ‘red’;

  1. ```typescript
  2. let element: HTMLElement|null = document.getElementById('root');
  3. element!.style.color = 'green'; //非空断言操作符
  4. //编译为
  5. var element = document.getElementById('root');
  6. element.style.color = 'green';

keyof any 只是 string、number、symbol 的联合类型,any可以赋值给任意类型
  1. type KeyofAny = keyof any;
  2. // => type KeyofAny = string | number | symbol

null、undefined

  • null 和 undefined 是其它类型的子类型,可以赋值给其它类型,如数字类型,此时,赋值后的类型会变成 null 或 undefined 。
  • strictNullChecks 参数用于新的严格空检查模式,在严格空检查模式下,null 和 undefined 值都不属于任何一个类型,它们只能赋值给自己这种类型或者any 。 ```typescript let x:number; x = 1; x = undefined; x = null; console.log(x); //null

//编译为 var x; x = 1; x = undefined; x = null;

  1. ```typescript
  2. // 如果开启 strictNullChecks 空检查模式,则不能把 null 和 undefined 赋值给上面的 x
  3. // 只能像下面这样写:
  4. // y 或者是number类型,或者是null类型,或者是undefiend类型
  5. let y :number|null|undefined;
  6. y = 1;
  7. y = undefined;
  8. y = null;
  9. let z1:undefined = undefined;
  10. let z2:null = null;
  11. let z3:any = undefined;
  12. let z4:any = null;

void 类型

  • void 表示没有任何类型,只能接受null,undefined。
  • 通常用于:当一个函数没有返回值时,TS 会认为它的返回值是 void 类型。
  • 严格模式下不能将null赋予给void
    1. function greeting(name:string):void{
    2. console.log('hello', name);
    3. // 当我们声明一个变量类型是 void 的时候,
    4. // 它的非严格模式(strictNullChecks: false)下仅可以被赋值为 null 和 undefined
    5. // 严格模式(strictNullChecks: true)下只能返回 undefined
    6. // return null; //报错
    7. return undefined;
    8. }

never 类型

never 是其它类型(null、undefined)的子类型,代表不会出现的值。

  1. // 1、作为 不会返回的函数 的返回值 类型
  2. function error1(message:string){} //这种其实还是有返回值的,它的返回值是 undefined
  3. console.log(error1('s')); //undefined
  4. // function error2(message:string):never{} //报错
  5. // 返回never的函数 必须存在 无法达到( unreachable ) 的终点
  6. function error3(message:string):never{
  7. throw new Error('报错了'); //直接异常结束了,就永远不可能出现返回值
  8. console.log('ok');
  9. }
  10. // 由类型推论得到返回值为 never
  11. function fail() {
  12. return error3("Something failed");
  13. }
  14. let result = fail();
  15. // 2、写一个死循环,无法终止,也就是永远没有返回值。
  16. function loop():never{
  17. while(true){
  18. }
  19. console.log('ok');
  20. }

strictNullChecks

  • 在 TS 中, null 和 undefined 是任何类型的有效值,所以无法正确地检测它们是否被错误地使用。于是 TS 引入了 —strictNullChecks 这一种检查模式
  • 由于引入了 —strictNullChecks ,在这一模式下,null 和 undefined 能被检测到。所以 TS 需要一种新的底部类型( bottom type )。所以就引入了 never。

    1. // Compiled with --strictNullChecks
    2. function fn(x: number | string) {
    3. if (typeof x === 'number') {
    4. // x: number 类型
    5. } else if (typeof x === 'string') {
    6. // x: string 类型
    7. } else {
    8. // x: never 类型
    9. // --strictNullChecks 模式下,这里的代码将不会被执行,x 无法被观察
    10. // 这可以帮我们做完整性校验,如果代码能走到这里,说明我们代码是有问题的
    11. }
    12. }

    never 和 void 的区别

  • void 可以被赋值为 null 和 undefined 的类型;never 则是一个不包含值的类型。

  • 拥有 void 返回值类型的函数能正常运行;拥有 never 返回值类型的函数无法正常返回,无法终止,或会抛出异常。

Symbol 类型

  • 我们在使用 Symbol 的时候,必须添加 es6 编译辅助库
  • Symbol 是在 ES2015 之后成为新的原始类型,它通过 Symbol 构造函数创建
  • Symbol 的值时唯一不变的
    将 lib 编译器选项更改为 es2015 或 更高版本
    1. {
    2. "compilerOptions": {
    3. /*指定编译的时候用来包含的编译文件*/
    4. "lib": ["dom", "ESNext"],
    5. }
    6. }
    1. const sym1 = Symbol('key');
    2. const sym2 = Symbol('key');
    3. console.log(sym1 === sym2);

BigInt 类型

  • 使用 BigInt 可以安全存储和操作大整数
  • 我们在使用 BigInt 的时候,必须添加 ESNext 的编译辅助库
  • 要使用 1n 需要 target: "ESNext"
  • number 和 bigint 类型不一样,不兼容 ```dart const max = Number.MAX_SAFE_INTEGER; //2**53 -1 console.log(max+1 === max+2); //true

const max2:bigint = BigInt((Number as any).MAX_SAFE_INTEGER); console.log(max2 + BigInt(1) === max2 + BigInt(2)); //false

  1. ```typescript
  2. //number 和 bigint 类型不一样,不兼容
  3. //JS里的类型是Number BigInt; ts里的类型是 number bigint
  4. let foo: number;
  5. let bar: bigint;
  6. foo = bar; //报错
  7. bar = foo; //报错

类型推论

  • 是指编程语言中能够自动推导出值的类型的能力,它是一些强静态类型语言中出现的特性。
  • 定义时未赋值就会推论成 any 类型
  • 如果定义的时候就赋值就能利用到类型推论 ```typescript // 定义时未赋值就会推论成 any 类型 let s1; s1 = 1; s1 = ‘zcy’; s1 = true;

// 如果定义的时候就赋值就能利用到类型推论 let s2 = ‘zcy’; s2 = ‘hello’; // s2 = 1; //报错,只能赋值为字符串类型

  1. <a name="60Wzi"></a>
  2. # 包装对象(Wrapper Object)
  3. - JavaScript 的类型分为2种:原始数据类型(Primitive data types)和对象类型(Object types)。
  4. - 所有的原始数据类型都没有属性(property)
  5. - 原始数据类型
  6. - 布尔值
  7. - 数值
  8. - 字符串
  9. - null
  10. - undefined
  11. - Symbol
  12. ```typescript
  13. let n1 = 'jack';
  14. console.log(n1.toLocaleUpperCase());
  15. // string 是原始数据类型,为什么能调用属性方法呢?
  16. // 因为 js内部 自动帮你包装成对象类型。
  17. // 即:当调用基本数据类型方法的时候,js 会在原始数据类型和对象类型之间做一个迅速的强制性切换
  18. // 如下:
  19. console.log( new String(n1).toLocaleUpperCase() );
  20. let flag1:boolean = true;
  21. let flag2:boolean = Boolean(1); //Boolean(1) 返回是true,还是原始数据类型boolean,可以赋值
  22. let flag3:boolean = new Boolean(1);
  23. //报错:new Boolean(1) 就是一个对象类型,不能赋值给boolean类型的变量。

联合类型(Union Types)

  • 联合类型表示取值可以为多种类型中的一种.
    1. let xx:string | number;
    2. xx = 1;
    3. xx = 'jack';
    未赋值时联合类型上只能访问两个类型共有的属性和方法。
    image.png
    如果赋值类型后,可以根据上下文自动推断对应类型的方法。
    image.png
    image.png ```typescript let ele: HTMLElement|null = document.getElementById(‘root’); // ele.innerHTML = ‘jack’; //报错,ele可能为null

// 普通处理 if (ele) ele.innerHTML = ‘jack’;

// ! 非空断言,告诉ts,ele一定不为空(null 或 undefined),不要给我报错;如果出错了,我自负后果。 // 因此,就可以执行这两个值的类型所阻止的操作 ele!.innerHTML = ‘jack’;

// 直接强转某个类型,把ele强转为 HTMLElement 类型 (ele).innerHTML = ‘jack’; //已过时 (ele as HTMLElement).innerHTML = ‘jack’;

// 非空断言也可以写到后面 let ele: HTMLElement|null = document.getElementById(‘root’)!; ele.innerHTML = ‘jack’; //报错,ele可能为null

  1. <a name="ibvmI"></a>
  2. # 类型断言
  3. - 类型断言可以将一个联合类型的变量,指定为一个更加具体的类型。
  4. - 不能将联合类型断言为不存在的类型。
  5. - **类型断言更像是类型的选择,而不是类型转换**
  6. <br />
  7. <a name="6kIWD"></a>
  8. ##### 使用类型断言有2种方式:
  9. ```typescript
  10. // '尖括号'语法:<类型>值 (该语法已经过时)
  11. // 'as'语法:值 as 类型 (推荐这种方式,因为 jsx 这样的语法中只支持 as 方式。)


示例说明:
  1. function func(val:string|number):number {
  2. if (val.length) { //报错
  3. return val.length;
  4. } else {
  5. return val.toString().length;
  6. }
  7. }

函数的参数 val 是一个联合类型,函数要返回参数的长度,但是 length 可以是字符串的属性,而数值是没有这个属性的,所以当 val 是数值时,就先用 toString() 先将数字转换成字符串再取长度。逻辑本身没有问题,但是在编译阶段一访问 val.length 时就报错了,因为 访问联合类型值的的属性时,这个属性必须是所有可能类型的公有属性,而 length 不是共有属性,val 的类型此时也没确定,所以编译不通过。为了通过编译,此时就可以使用功能类型断言了:

  1. function func(val:string|number):number {
  2. if ((<string>val).length) {
  3. return (<string>val).length;
  4. } else {
  5. return val.toString().length;
  6. }
  7. }
  8. // 或
  9. function func(val:string|number):number {
  10. //把val强行断言为string类型,此时就可以访问length属性了。
  11. if ((val as string).length) {
  12. return (val as string).length;
  13. } else {
  14. return val.toString().length;
  15. }
  16. }
  17. //如果val断言为了string,那么开始定义的联合类型是不是就失去了它的意义?
  18. //否。类型断言不是类型转换,而是类型选择,可以理解为在编译阶段强行把val当做string类型来访问了。

双重断言

尽量不要使用双重断言,会破坏原有类型关系,断言为any是因为any类型可以被赋值给其他类型

  1. let s1: string|number;
  2. console.log((s1! as number).toFixed(2)); //强行断言 s1非空,并且为数字类型
  3. console.log((s1! as string).length); //强行断言 s1非空,并且为字符串类型
  4. ((<number>s1!).toFixed(2)); //这种尽量不要使用,因为在react中会被认为是jsx语法
  5. // console.log((s1! as boolean));
  6. // 不能将联合类型断言为不存在的类型。
  7. // 只能双重断言,先转成 any,再转成 boolean
  8. console.log( (s1! as any) as boolean );

? 链判断运算符
  1. let ele: HTMLElement|null = document.getElementById('root');
  2. // ? 链判断运算符(es7新增, 不是ts的)
  3. ele?.style?.color
  4. // 等同于 ele && ele.style && ele.style.color

非空断言运算符(后缀 !

如果值的类型是包含 undefinednull 类型的联合,则 non-nullish声明运算符(或 non-null 声明运算符)将从联合类型中删除这些类型。我们告诉 TypeScript:“这个值不能是 undefinednull。”因此,我们可以执行这两个值的类型所阻止的操作,例如:

  1. let x = 'Jane' as (null|undefined|string);
  2. console.log(x.length); //报错,变量x可能为 null类型 或者 undefined类型。
  3. console.log(x!.length); //ok,声明x不能是 null 或者 undefined

案例: Maps: .has() 之后的 .get()

使用 Map 方法 .has() 之后,我们知道 Map 具有给定的键。遗憾的是,.get() 的结果不能反映这一点,这就是为什么我们必须使用 nullish 断言运算符的原因:

  1. function getLength(strMap: Map<string, string>, key: string): number {
  2. if (strMap.has(key)) {
  3. // We are sure x is not undefined:
  4. const value = strMap.get(key)!; // (A)
  5. return value.length;
  6. }
  7. return -1;
  8. }

由于 strMap 的值永远不会是 undefined,因此我们可以通过检查 .get() 的结果是否为 undefined 来检测丢失的 Map 条目(A 行):

  1. function getLength(strMap: Map<string, string>, key: string): number {
  2. // %inferred-type: string | undefined
  3. const value = strMap.get(key);
  4. if (value === undefined) { // (A)
  5. return -1;
  6. }
  7. // %inferred-type: string
  8. value;
  9. return value.length;
  10. }

定值断言

如果打开 strict 属性初始化,有时需要告诉 TypeScript 我们确实初始化某些属性——即使它认为我们不需要这样做。

  1. namespace a{
  2. let x:string;
  3. let y:string;
  4. x = y; //报错,在赋值前使用了变量y
  5. }
  6. namespace b{
  7. let x!:string; //使用“定值分配断言”(感叹号)后,错误就会消失
  8. let y!:string; //强行断言x、y已经初始化赋值了
  9. x = y;
  10. }

字面量类型和类型字面量

  • 字面量类型要和实际的值的字面量一一对应,如果不一致就会报错。
  • 类型字面量和对象字面量的语法很相似
    字面量类型
    ```typescript // 下面的 ‘Up’、’Down’、’Left’、’Right’ 就是字面量类型,变量的值也只能为该字符串 const up:’Up’ = ‘Up’; //变量 up 被指定为 ‘Up’类型,其值只能为 字符串’Up’ const down:’Down’ = ‘Down’; const left:’Left’ = ‘Left’; const right:’Right’ = ‘Right’;

// 用法示例: // 声明一个类型 Direction,该类型可以为 ‘Up’、’Down’、’Left’、’Right’ 这几种字面量类型。 type Direction = ‘Up’ | ‘Down’ | ‘Left’ | ‘Right’; // 函数参数direction的值只能是 ‘Up’、’Down’、’Left’、’Right’ 这几种字面量类型 // 可以实现枚举的效果, function move (direction: Direction){} move(‘Down’); move(down);

  1. <a name="M6F24"></a>
  2. ##### 类型字面量
  3. ```typescript
  4. // 声明一个Person类型
  5. type Person = {
  6. name:string,
  7. age:number,
  8. }
  9. let p1:Person = {
  10. name: 'zcy', //name的值必须为字符串类型
  11. age: 18, //age的值必须为数字类型
  12. }
  13. // let p2:Person = {name: 'acy'}; //报错
  14. // 并且 p1的属性 name age 属性都不能缺少,否则就会报错

字符串字面量和联合类型

  • 字符串字面量类型用来约束取值只能是某 几个字符串 中的一个,联合类型表示取值可以为 多种类型 中的一种
  • 字符串字面量限定了使用该字面量的地方仅接受特定的值,联合类型对于值并没有限定,仅仅限定值的类型需要保持一致。 ```typescript type T1 = ‘1’ | ‘2’ | ‘3’; type T2 = string | number | boolean;

let t1:T1 = ‘1’; //t1 只能是’1’或’2’或’3’ let t2:T2 = true; // t2 可以是字符串、数字、布尔值。 ```