命名空间 namespace
一般在一个文件里,重复声明变量是不允许的,也会报错。
let x = 1;let x = 2; //报错:无法重新声明块范围变量“x”class Person{}class Person{} //报错
我们可以用命名空间来解决,这样各自就不会冲突了。
namespace a{let x = 1;class Person{}}namespace b{let x = 2;class Person{}}//编译为var a;(function (a) {var x = 1;})(a || (a = {}));var b;(function (b) {var x = 2;})(b || (b = {}));
数据类型
布尔、数字、字符串类型
let p1:boolean = true;let p2:(boolean) = false; //括号加不加都行let n1:number = 1;let s1:string = 'jack';//编译为var p1 = true;var p2 = false; //括号加不加都行var n1 = 1;var s1 = 'jack';
number 和 Number 的区别
- number 是基础类型
- Number 是number的封装类,是包装器对象类型
let n1:number = 1;let n2:Number = 1; //用来描述实例的,类也可以当做类型let n3:number = Number(1); //Number(1) 还是一个数字类型,numberlet n4:Number = new Number(1); //new Number(1)就是一个对象类型,只能用 大Number 类型
数组类型(array)
let p1:number[] = [1,2,3];let p2:string[] = ['a', 'b', 'c'];let p3:(number|string)[] = [1, 'b', 3];//泛型方式来声明let p4:Array<number> = [1,2,3];let p5:Array<number | string> = [1, 'b', 3];//编译为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];
<a name="qIrUP"></a>##### 元祖与数组的区别| **元祖** | **数组** || --- | --- || 每一项可以是不同的类型 | 每一项都是同一种类型 || 有预定义的长度 | 没有长度限制 || 用于表示一个固定的结构 | 用于表示一个列表 |<a name="c8e1e31f"></a>## 对象类型(object)```typescriptlet a1:object = 1; //errorlet a2:object = { name: 'jack' }; //okinterface Obj {[key:string]: any;};let b1:Obj = 1; //errorlet b2:object = { name: 'jack' }; //ok
函数类型(Function)
注意首字母是大写
let fn = function(){};let a1:Function = 1; //errorlet a2:Function = fn; //oktype Fn = (...args: any) => any;let b1:Function = 1; //errorlet b2:Function = fn; //ok
枚举类型
事先考虑某一个变量的所有的可能的值,尽量用自然语言中的单词表示它的每一个值。
比如性别、月份、星期、颜色、单位、学历
普通枚举
enum Gender {GIRL, // 默认从0开始BOY,}console.log(Gender);// 从结果来看: 可以枚举,也可以反举// {// 0: "GIRL",// 1: "BOY",// GIRL: 0,// BOY: 1,// }// 编译为var Gender;(function (Gender) {Gender[Gender["GIRL"] = 0] = "GIRL"; /*先把0赋给Gender["GIRL"], 再把"GIRL"赋给Gender[0]*/Gender[Gender["BOY"] = 1] = "BOY";})(Gender || (Gender = {}));
异构枚举
enum Animal {BIRD = 'bird',DOG = 1,CAT,}console.log(Animal);// {// 1: "DOG"// 2: "CAT"// BIRD: "bird"// CAT: 2// DOG: 1// }// 编译为var Animal;(function (Animal) {Animal["BIRD"] = "bird";Animal[Animal["DOG"] = 1] = "DOG";Animal[Animal["CAT"] = 2] = "CAT";})(Animal || (Animal = {}));
常量枚举
常量枚举与普通枚举的区别是,它会在编译阶段被删除,并且不能包含计算成员。
假如包含了计算成员,则会在编译阶段报错。
//注意,前边加了一个 const。加上后,就不会生成对象了,但是也不支持反举了const enum Colors {RED,YELLOW,BLUE,}let myColor = [Colors.RED, Colors.YELLOW, Colors.BLUE];console.log(myColor); //[ 0, 1, 2 ]// 编译后var myColor = [0 /* RED */, 1 /* YELLOW */, 2 /* BLUE */];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’;
```typescriptlet element: HTMLElement|null = document.getElementById('root');element!.style.color = 'green'; //非空断言操作符//编译为var element = document.getElementById('root');element.style.color = 'green';
keyof any 只是 string、number、symbol 的联合类型,any可以赋值给任意类型
type KeyofAny = keyof any;// => 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;
```typescript// 如果开启 strictNullChecks 空检查模式,则不能把 null 和 undefined 赋值给上面的 x// 只能像下面这样写:// y 或者是number类型,或者是null类型,或者是undefiend类型let y :number|null|undefined;y = 1;y = undefined;y = null;let z1:undefined = undefined;let z2:null = null;let z3:any = undefined;let z4:any = null;
void 类型
- void 表示没有任何类型,只能接受null,undefined。
- 通常用于:当一个函数没有返回值时,TS 会认为它的返回值是 void 类型。
- 严格模式下不能将null赋予给void
function greeting(name:string):void{console.log('hello', name);// 当我们声明一个变量类型是 void 的时候,// 它的非严格模式(strictNullChecks: false)下仅可以被赋值为 null 和 undefined// 严格模式(strictNullChecks: true)下只能返回 undefined// return null; //报错return undefined;}
never 类型
never 是其它类型(null、undefined)的子类型,代表不会出现的值。
// 1、作为 不会返回的函数 的返回值 类型function error1(message:string){} //这种其实还是有返回值的,它的返回值是 undefinedconsole.log(error1('s')); //undefined// function error2(message:string):never{} //报错// 返回never的函数 必须存在 无法达到( unreachable ) 的终点function error3(message:string):never{throw new Error('报错了'); //直接异常结束了,就永远不可能出现返回值console.log('ok');}// 由类型推论得到返回值为 neverfunction fail() {return error3("Something failed");}let result = fail();// 2、写一个死循环,无法终止,也就是永远没有返回值。function loop():never{while(true){}console.log('ok');}
strictNullChecks
- 在 TS 中, null 和 undefined 是任何类型的有效值,所以无法正确地检测它们是否被错误地使用。于是 TS 引入了 —strictNullChecks 这一种检查模式
由于引入了 —strictNullChecks ,在这一模式下,null 和 undefined 能被检测到。所以 TS 需要一种新的底部类型( bottom type )。所以就引入了 never。
// Compiled with --strictNullChecksfunction fn(x: number | string) {if (typeof x === 'number') {// x: number 类型} else if (typeof x === 'string') {// x: string 类型} else {// x: never 类型// --strictNullChecks 模式下,这里的代码将不会被执行,x 无法被观察// 这可以帮我们做完整性校验,如果代码能走到这里,说明我们代码是有问题的}}
never 和 void 的区别
void 可以被赋值为 null 和 undefined 的类型;never 则是一个不包含值的类型。
- 拥有 void 返回值类型的函数能正常运行;拥有 never 返回值类型的函数无法正常返回,无法终止,或会抛出异常。
Symbol 类型
- 我们在使用 Symbol 的时候,必须添加 es6 编译辅助库
- Symbol 是在 ES2015 之后成为新的原始类型,它通过 Symbol 构造函数创建
- Symbol 的值时唯一不变的
将 lib 编译器选项更改为 es2015 或 更高版本
{"compilerOptions": {/*指定编译的时候用来包含的编译文件*/"lib": ["dom", "ESNext"],}}
const sym1 = Symbol('key');const sym2 = Symbol('key');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
```typescript//number 和 bigint 类型不一样,不兼容//JS里的类型是Number BigInt; ts里的类型是 number bigintlet foo: number;let bar: bigint;foo = bar; //报错bar = foo; //报错
类型推论
- 是指编程语言中能够自动推导出值的类型的能力,它是一些强静态类型语言中出现的特性。
- 定义时未赋值就会推论成 any 类型
- 如果定义的时候就赋值就能利用到类型推论 ```typescript // 定义时未赋值就会推论成 any 类型 let s1; s1 = 1; s1 = ‘zcy’; s1 = true;
// 如果定义的时候就赋值就能利用到类型推论 let s2 = ‘zcy’; s2 = ‘hello’; // s2 = 1; //报错,只能赋值为字符串类型
<a name="60Wzi"></a># 包装对象(Wrapper Object)- JavaScript 的类型分为2种:原始数据类型(Primitive data types)和对象类型(Object types)。- 所有的原始数据类型都没有属性(property)- 原始数据类型- 布尔值- 数值- 字符串- null- undefined- Symbol```typescriptlet n1 = 'jack';console.log(n1.toLocaleUpperCase());// string 是原始数据类型,为什么能调用属性方法呢?// 因为 js内部 自动帮你包装成对象类型。// 即:当调用基本数据类型方法的时候,js 会在原始数据类型和对象类型之间做一个迅速的强制性切换// 如下:console.log( new String(n1).toLocaleUpperCase() );let flag1:boolean = true;let flag2:boolean = Boolean(1); //Boolean(1) 返回是true,还是原始数据类型boolean,可以赋值let flag3:boolean = new Boolean(1);//报错:new Boolean(1) 就是一个对象类型,不能赋值给boolean类型的变量。
联合类型(Union Types)
- 联合类型表示取值可以为多种类型中的一种.
let xx:string | number;xx = 1;xx = 'jack';
未赋值时联合类型上只能访问两个类型共有的属性和方法。
如果赋值类型后,可以根据上下文自动推断对应类型的方法。

```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 类型
(
// 非空断言也可以写到后面 let ele: HTMLElement|null = document.getElementById(‘root’)!; ele.innerHTML = ‘jack’; //报错,ele可能为null
<a name="ibvmI"></a># 类型断言- 类型断言可以将一个联合类型的变量,指定为一个更加具体的类型。- 不能将联合类型断言为不存在的类型。- **类型断言更像是类型的选择,而不是类型转换**<br /><a name="6kIWD"></a>##### 使用类型断言有2种方式:```typescript// '尖括号'语法:<类型>值 (该语法已经过时)// 'as'语法:值 as 类型 (推荐这种方式,因为 jsx 这样的语法中只支持 as 方式。)
示例说明:
function func(val:string|number):number {if (val.length) { //报错return val.length;} else {return val.toString().length;}}
函数的参数 val 是一个联合类型,函数要返回参数的长度,但是 length 可以是字符串的属性,而数值是没有这个属性的,所以当 val 是数值时,就先用 toString() 先将数字转换成字符串再取长度。逻辑本身没有问题,但是在编译阶段一访问 val.length 时就报错了,因为 访问联合类型值的的属性时,这个属性必须是所有可能类型的公有属性,而 length 不是共有属性,val 的类型此时也没确定,所以编译不通过。为了通过编译,此时就可以使用功能类型断言了:
function func(val:string|number):number {if ((<string>val).length) {return (<string>val).length;} else {return val.toString().length;}}// 或function func(val:string|number):number {//把val强行断言为string类型,此时就可以访问length属性了。if ((val as string).length) {return (val as string).length;} else {return val.toString().length;}}//如果val断言为了string,那么开始定义的联合类型是不是就失去了它的意义?//否。类型断言不是类型转换,而是类型选择,可以理解为在编译阶段强行把val当做string类型来访问了。
双重断言
尽量不要使用双重断言,会破坏原有类型关系,断言为any是因为any类型可以被赋值给其他类型
let s1: string|number;console.log((s1! as number).toFixed(2)); //强行断言 s1非空,并且为数字类型console.log((s1! as string).length); //强行断言 s1非空,并且为字符串类型((<number>s1!).toFixed(2)); //这种尽量不要使用,因为在react中会被认为是jsx语法// console.log((s1! as boolean));// 不能将联合类型断言为不存在的类型。// 只能双重断言,先转成 any,再转成 booleanconsole.log( (s1! as any) as boolean );
? 链判断运算符
let ele: HTMLElement|null = document.getElementById('root');// ? 链判断运算符(es7新增, 不是ts的)ele?.style?.color// 等同于 ele && ele.style && ele.style.color
非空断言运算符(后缀 !)
如果值的类型是包含 undefined 或 null 类型的联合,则 non-nullish声明运算符(或 non-null 声明运算符)将从联合类型中删除这些类型。我们告诉 TypeScript:“这个值不能是 undefined 或 null。”因此,我们可以执行这两个值的类型所阻止的操作,例如:
let x = 'Jane' as (null|undefined|string);console.log(x.length); //报错,变量x可能为 null类型 或者 undefined类型。console.log(x!.length); //ok,声明x不能是 null 或者 undefined
案例: Maps: .has() 之后的 .get()
使用 Map 方法 .has() 之后,我们知道 Map 具有给定的键。遗憾的是,.get() 的结果不能反映这一点,这就是为什么我们必须使用 nullish 断言运算符的原因:
function getLength(strMap: Map<string, string>, key: string): number {if (strMap.has(key)) {// We are sure x is not undefined:const value = strMap.get(key)!; // (A)return value.length;}return -1;}
由于 strMap 的值永远不会是 undefined,因此我们可以通过检查 .get() 的结果是否为 undefined 来检测丢失的 Map 条目(A 行):
function getLength(strMap: Map<string, string>, key: string): number {// %inferred-type: string | undefinedconst value = strMap.get(key);if (value === undefined) { // (A)return -1;}// %inferred-type: stringvalue;return value.length;}
定值断言
如果打开 strict 属性初始化,有时需要告诉 TypeScript 我们确实初始化某些属性——即使它认为我们不需要这样做。
namespace a{let x:string;let y:string;x = y; //报错,在赋值前使用了变量y}namespace b{let x!:string; //使用“定值分配断言”(感叹号)后,错误就会消失let y!:string; //强行断言x、y已经初始化赋值了x = y;}
字面量类型和类型字面量
- 字面量类型要和实际的值的字面量一一对应,如果不一致就会报错。
- 类型字面量和对象字面量的语法很相似
字面量类型
```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);
<a name="M6F24"></a>##### 类型字面量```typescript// 声明一个Person类型type Person = {name:string,age:number,}let p1:Person = {name: 'zcy', //name的值必须为字符串类型age: 18, //age的值必须为数字类型}// let p2:Person = {name: 'acy'}; //报错// 并且 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 可以是字符串、数字、布尔值。 ```
