1.数据类型
基础数据类型
- 布尔值Boolean:
let isDone: boolean = false;
- 数字Number:
let decLiteral: number = 6;
支持2(0b),8(0o),10,16(0x)进制 - 字符串String:
let name: string = "bob";
可以使用单引号和双引号
可以使用模板字符串 - null:只能赋值为null,
let n:null=null;
但是null可以赋值给其他类型let s: string = null
- undefined:只能赋值为undefined,
let u: undefined = undefined
但是null可以赋值给其他类型let n: number = undefined
复杂数据类型
- 数组Array:TS的数组只能存放一种类型的数据,有两种定义方式
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
- 元组 Tuple:将不同类型的数据合并在一起
1.直接通过[]赋值时,[]中元素的数量和类型要和定义的完全对应。let t:[string,number]=["王章昊",22];
2.如果通过索引访问或者重新定义也要在初始化以后let t:[string,number];
t[0]=”王章昊”;//报错t is undefined
3.不允许越界访问了,版本4.0.3,中文文档上还是写的可以 - 枚举enmu:enmu Color{Red,Blue};let bg:Color=Color.Red
枚举中的定义的值只是数字的一个代替,打印bg可以看到输出0,它默认是从0开始的,我们可以通过为第一个枚举值设置值来改变起始值
enmu Color{Red=10,Blue};let bg:Color=Color.Red - 对象Object:同JS
抽象类型
- void:JavaScript没有空值概念,TS中空值代表没有任何返回值,常用于设置函数返回值
function alterName():void{alter("王章昊")}
,
声明一个void
类型的变量没有什么用,因为只能将它赋值为undefined
和null
- any:任意值,表示允许赋值为任意类型。
1.普通类型在赋值过程中不能改变变量的类型,例如:let n:number=1;n="2";
//error:Type 'string' is not assignable to type 'number'
2.对于any类型,我们在初始化和重新赋值的时候可以将其赋值为任何类型的值let age: any = 23; age = "24";
3.当一个变量被声明为any,它可以访问任何属性和方法,即便并不存在,例如:let a: any = 4; a.toLowerCase(); a.name;
ts编译器会认为any
可以访问其他基本类型的方法和属性。但是访问不存在的属性和方法在运行时会报错
4.通过any类型会通过对象的属性访问传播let obj: any = { name: "王章昊", age: 22 }; obj.name = 2020; obj.age = "23"
;也就是声明了any的对象内部的属性也是any,可以改变类型赋值。如果去掉any编译时会报错
5.默认值,在定义一个变量时如果不指定类型且没有在定义时赋值,则类型默认any;let a; a = 1; a = 12; a = "sfasd";
如果没指定类型但是在定义时赋值了,则变量的类型默认是赋值的类型,let b = 1; b = "string";
//error :Type ‘string’ is not assignable to type ‘number’
6.使用TypeScript的主要动机之一就是类型安全,因此应该尽量避免使用any类型定义变量 - Unknown:未知值,允许赋值为任意类型,与any的主要区别是不可以随时访问任何的属性和方法。
let a: unknown = "12"; console.log(a.slice(1));
//error: Property ‘slice’ does not exist on type ‘unknown’
但是我们可以在通过了类型检查后使用该类型的方法
let a: unknown = "12";
if (typeof a === "string") {
console.log(a.slice(1));
a.toFixed(2);//error:Property 'toFixed' does not exist on type 'string'.
}
if (typeof a === "number") {
console.log(a.slice(1));//error:Property 'slice' does not exist on type 'number'
console.log(a.toFixed(2))
}
- never:表示变量never occur,也就是永远没有结果。用never定义一个变量
let a:never;
该变量永远不能被赋值,包括null,undefined。定义函数返回值
表示函数不可能正常结束,例如
// 抛出异常
function error(message: string): never {
throw new Error(message);
}
//无限循环
function infiniteLoop(): never {
while (true) {}
}
// return的类型同样是个never
function fail() {
//调用上面的error函数
return error("Something failed");
}
2.联合类型
表示取值可以是多种类型中的一个
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';//OK
myFavoriteNumber = 7;//OK
myFavoriteNumber = true;//ERROR:Type 'boolean' is not assignable to type 'string | number'
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法。
function getString(something: string | number): string {
return something.toString();//OK
}
function getLength(something: string | number): number {
return something.length;
//ERROR:Property 'length' does not exist on type 'string | number'. Property 'length' does not exist on type 'number'
}
当已经确定了变量类型为联合类型中的一个,访问不属于该类型的变量或方法会报错
let n: number | string = "three";
console.log(n.length)//OK
n = 7
console.log(n.length)//ERROR:Property 'length' does not exist on type 'number'
3.类型推断与类型断言
类型推断
当我们在定义变量或者函数的返回值时,如果没有明确的写明类型,那编译器会根据函数返回值或者变量的赋值情况自动推断出变量类型
对于函数
// a b 值为any,result也就是any,因此函数返回值值也是any
function connect(a, b){
let result = a + b
return result
}
let result: number = connect(1, 2)
let result2: string = connect("1", "2")
// 如果确定返回值是number,
function connect(a:number, b:number){
let result = a + b
return result
}
let result: number = connect(1, 2)
let result2: string = connect("1", "2") //error
对于变量
//因为定义变量是赋值为了数字,则变量类型就成了number,重新赋值为其他类型时报错
let age=23;
age="23";//Type 'string' is not assignable to type 'number'
//通过表达式或函数赋值
let a1: string = "string"
let a2: number = 12
let a3 = a1 + a2;//string
a3 = 1241324//error:Type 'number' is not assignable to type 'string'
// 定义变量是没有立即赋值,则推断为any,后续再赋值仍为any
let age;
age=23;
age="24";//不影响其他类型的赋值
类型断言
将一个不明确的类型手动确定为更加具体的类型
值 as 类型 或 <类型>值
1.将联合类型断言为一个具体的类型
//方程的返回值是联合类型
function getResult(a, b): number | string {
return a + b
}
直接将联合类型赋值给一个具体类型会报错,即便我们知道方程结果和变量的类型是符合的
let result1: number = getResult(1, 2)//error:Type 'string | number' is not assignable to type 'number'. Type 'string' is not assignable to type 'number'
let result2: string = getResult("1", "2")//error :Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'
//此时我们可以使用断言,将方程返回的联合类型断言为一个确定的类型
let result1: number = getResult(1, 2) as number
let result2: string = getResult("1", "2") as string
//或者
let result1: string = <string>getResult("1", "2")
let result2: number = <number>getResult(1, 2)
//注意 将一个联合类型断言为一个不在联合类型中的类型编译会报错
let result3: boolean = <boolean>getResult(1, 2);
//error:Conversion of type 'string | number' to type 'boolean' may be a mistake because neither type sufficiently overlaps with the other.
//If this was intentional, convert the expression to 'unknown' first.
//Type 'number' is not comparable to type 'boolean'.
2.将unknown断言为具体类型
let a: unknown = "asdfasdf";
console.log(a.length)//Property 'length' does not exist on type 'unknown'
console.log((a as string).length)
3.将any断言为具体类型
下面的例子中,我们调用完 getCacheData
之后,立即将它断言为 Cat
类型。这样的话明确了 tom
的类型,后续对 tom
的访问时就有了代码补全,提高了代码的可维护性
interface Cat {
name: string;
run(): void;
}
function getCacheData(key: string): any {
return (window as any).cache[key];
}
const tom = getCacheData('tom') as Cat;
tom.run();
关于断言细节的东西请阅读TypeScript类型断言。
4.TypeScript中的接口。
在大部分面向对象语言中,接口(Interfaces)是作为行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。
但是在TypeScript中,接口是一个灵活的概念,除了对类的行为进行抽象,还可以作为对象形状的描述
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
};
上面的例子中我们先定义了接口Person,Person中定义了字段name和age,分别是string和number类型。然后定了tom变量,类型时Person,因此tom对象中的字段和类型要和Person中定义的一致。如果我们在tom对象中再加一个字段sex,编译时就会报错error:Object literal may only specify known properties, and ‘sex’ does not exist in type ‘Person’。对象中的变量的数量和类型要恰好和接口中定义的一致,不能多不能少也不能类型不符。
定义可选属性
属性名和类型中间用?:
分割,表示在定义对象时这个变量可以没有,但是仍不能添加额外的属性
interface Person {
name: string;
age?: number;
}
let tom: Person = {
name: 'Tom',
age: 25
};
//OK
let jerry:Person={
name:"Jerry"
}
//ERROR
let jerry:Person={
name:"Jerry",
sex:"男"
}
定义任意属性
interface Person {
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
age: 25,
sex:"男"
};
通过上面对Person的定义,我们可以很方便的对对象进行扩展。
需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型必须是任意属性的子集。
上面我们定义的任意属性的类型any,它包含了string和numbe。除了把任意属性定义为any外,我们还可以使用联合类型。
interface Person {
name: string;
age?: number;
[propName: string]: string | number;
}
定义只读属性
如果我们希望对象中的一些字段只能在创建时被赋值,之后就不能再修改。那么我们可以用readonly来定义属性
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: string | number;
}
let tom: Person = {
id: 89757,
name: 'Tom',
sex: '男'
};
tom.id = 9527;// error:Cannot assign to 'id' because it is a read-only property.
需要注意的是,readonly对属性赋值的限制只是存在于第一次给对象赋值,而不是第一次给这个属性赋值时。