基础类型
js 弱类型语言,类型可以随意地去改变,得益于该特点让 js 代码从书写层面变得更灵活;
由于弱类型语言 缺乏类型约束,带来了诸多不便。
✅ 举个例子
JavaScript
function getDes(name: "张三" | "王五") {
return name === "张三" ? "人家永远18岁" : "我已年过半百的小老头";
}
getDes("张三"); // '人家永远18岁'
getDes("赵四"); // 输出却是:’我已年过半百的小老头‘ 不报错
typescript
function getDes(name: "张三" | "王五") {
return name === "张三" ? "人家永远18岁" : "我已年过半百的小老头";
}
getDes("张三"); // '人家永远18岁'
getDes("赵四"); // 输出却是:’我已年过半百的小老头‘ 报错
如果想让
js
代码与ts
代码同样的逻辑,需要给js
代码加入繁杂
的判断逻辑 增加了心智负担,同时还很有可能增加维护成本。
类型校验
下面代码在不使用类型校验时,函数传递字符串是可以被执行的
function sum(a, b) {
return a + b;
}
sum("1", 2); // 结果为'12'
加上严格类型校验,代码在编译环节会提示报错
function sum(a: number, b: number) {
return a + b;
}
sum(1, 2); // 2
sum("1", 2);
//报错 Argument of type 'string' is not assignable to parameter of type 'number'.
类型推断
当没有明确设置类型时,系统会根据值推断变量的类型
字符串
下面的实例中系统将 str
变量推断为 string
,当 str
被设置为 123
的 number
类型时将会报错
let str = "123"; // let str:string
str = 123; //报错 Type 'number' is not assignable to type 'string'.
数值
ts 中的数值类型包括了小数、负数、整数、(浮点数值 NaN
),会被推断为 number
类型
let num = 12; // let num:number
num = -100.1;
num = 101;
num = -Infinity;
num = NaN;
布尔值
值为 true
或 false
会被推断为 boolean
类型
let bool = true; // let bool:boolean
bool = false;
数组
下面的类型推断结果,表示数组值为字符串
let arr = ["1", "1"]; // let arr:string[]
arr.push("22"); // [1,'1','22']
arr.push(true);
//报错 Argument of type 'true' is not assignable to parameter of type ' string'.
下面的类型推到结果,表示数组值为数字
let arr = [1, 2, "3"]; // let arr:(string|number)[]
arr.push(22); // [1,2,'3',22]
arr.push(true);
//报错 Argument of type 'true' is not assignable to parameter of type 'string|number'.
对象
对象的字面量推导
const obj = { name: "true", sex: "nan", state: true };
// 推导结果如下
// const obj: {
// name: string,
// sex: string,
// state: boolean
// }
如果向对象中添加类型中不存在的属性将会报错
const user = { name: "zhangsan", age: 18, open: true };
user.city = "北京";
//将产生错误 Property 'city' does not exist on type
下面是更复杂的在对象中嵌套对象,TS 也是可以推断出来的
const user = {
name: "zhangsan",
age: 18,
open: true,
lessons: [{ title: "JS" }, { title: "TS" }],
};
user.lessons[0].title = "javascript";
上例推断的结果是
const user: {
name: string;
age: number;
open: boolean;
lessons: {
title: string;
}[];
}
配置文件
TS 支持自定义配置项
初始化
执行以下命令创建配置项
tsc --init # 初始化 tsconfig.json
然后执行命令使用自定义配置项进行监测
tsc -w
配置选项
配置 | 说明 |
---|---|
noImplicitAny | 禁止使用隐含的 any 类型,如函数参数没有设置具体类型 |
strictNullChecks | 开启时不允许将 null、undefined 赋值给其他类型比如字符串 |
target | 转换成 JS 的版本 |
strict | 是否严格模式执行 |
module | 使用的模块系统 |
基本类型
除了上面的类型自动推导外,更多的时候是设置明确的类型
字符串
使用 string
来声明字符串类型
let str: string = "123";
布尔
使用 boolean
来声明布尔类型
let bool: boolean = true;
数字
在 TS 中不区分浮点和整数,都采用 number 来声明
let num: number = 123;
数组
let arr: number[]=[1,2,3]; //数字类型
let arr2: string[]=['1','2','3'] //字符串型
let arr3 = new Array<string>(3) //使用泛型来声明
let arr4:Array<number>(3) = [0,1,2] //使用泛型来声明
元组
明确数组每个成员值类型的数组为元组
let tuple: [string, number] = ["1", 1]; //明确每个成员值类型的数组
对象
限制类型为对象
let obj: object = { name: "123", age: 12 }; //限制类型为对象
限制对象的值的类型
let obj2: { name: string, age: number } = { name: "123", age: 12 };
属性后面跟 ?.
用来指定参数可选,这样的属性参数为非必填项
let obj3: { name: string, age: number, happy?: string } = {
name: "sss",
age: 23,
}; // 属性值后面加? 表示可选属性
索引签名
如果有明确的索引名称
// 如果有明确的索引名称
type HH = { name: string; age: number };
let obj: HH = { name: "123", age: 12 };
如果没有明确的索引名称
// 如果没有明确的索引名称
type HH = { [key: string]: string | number; state: boolean };
let obj: HH = { name: "123", state: true };
带有后缀的索引
// 带有后缀的索引
type HH = { [key: string + HX]: string | number }};
let obj: HH = { nameHX: "123" };
// 或者使用模板字符串 带有后缀的索引
type HH = { [key: `${string}HX`]: string | number }};
let obj: HH = { nameHX: "123" };
使用 Record 工具类型来定义
// 使用Record工具类型来定义
type HH = Record<string, number | string>; // key为string 值为 number|string
let hd: HH = { name: "name", age: 12 };
使用 Record 声明联合类型
// 使用Record 声明联合类型
type HH = Record<"name" | "age", string | number>;
// key为 "name" 或者 "age", 值为 number|string
let hd: HH = { name: "name", age: 12 };
any
any 指包含所有值的顶部类型 所以 any 不进行类型校验,等于关闭了类型校验
- 其他类型是 any 类型的子集
- any 类型的变量可以赋值给其他类;
- 使用 any 类型失去了类型检查的作;
- 只有在不知道类型的时候才使用 any;
- any 太宽泛,不建议使用;
- any 不进行 ts 类型检测;
let anyy: any = [];
// 以下代码均可正常编译
anyy = {};
anyy = "as";
anyy = 12;
anyy = true;
anyy = () => {};
unknown
unknown 类型是所有类型的子类型,所以 unknown 类型的变量可以赋值给其他类;
- 任何类型都可以赋值给 unknown 类型;
- unknown 类型的变量不能赋值给其他类;
- 使用 unknown 类型可以进行类型检查;
- 使用 unknown 类型可以进行类型断言;
any 与 unknown 区别 as 类型断言
let y: string = "1";
let c: number = y as number; // 报错
// 可以使用 unknow 作为中间状态
//将string转化成 unkown类型 接着转换成 number类型
let n: number = y as unknown as number;
any 定义类型为 所有类型均可赋值,unknown 不知道是什么类型
用 any
声明 可以将变量看作 任何类型
,而 unknow
是不知道是什么类型 既不也不
let pp: any = "123";
let ps: unknown = "1234444";
//正确
let xml: string = pp; //any
//报错
let xml1: string = ps; //unknown
null & undefined
null 与 undefined 也属于变量类型
let aa: null = null;
let bb: underfined = undefined;
console.log(aa, bb);
void
- 非严格模式下,void 类型的变量可以赋值 null | undefined;
- 严格模式下,void 类型的变量只能赋值 undefined;
let aa: void = null; //严格模式下会报错
let bb: void = undefined;
void 不允许设置为其他类型
let aa: void = undefined;
aa = "str"; //将会报错
TS 中函数无任何返回内容,实际上返回的是 undefined
function foo:void(){
let str = 'str'
}
let fooPack = foo()
console.log(fooPack === undefined)
never
never 是任何类型的子类型 可赋值给任意类型 ,没有 类型是 never 的子类型
never 类型特点
- never 没有任何子类型 所以 never 类型的变量不能赋值给其他类型变量
- 函数
抛出异常
或无限循环
返回 never - never 类型没有任何的值
- never 类型是所有类型的子类
type HH = never extends string ? string : boolean; //string
type HH = string extends never ? string : boolean; //boolean
union 联合类型
union 联合类型多个类型的组合,使用 |
来连接
let hh: string | number = 12;
let arr: Array<string | number> = [12, "123"];
交叉类型
交叉类型是将 interface 、object 等进行组合,组合出新的类型
- 交叉类型 要保证类型的一致性,string 与 number 交叉将 得到 never 类型
- interface 、object 进行属性合并
类型交叉
type HH = { age: number };
type HB = { name: string; age: string }; // 等同于 number string的交叉
let jj: HH & HB = { age: "123", name: "123" }; // 报错 不能将类型string分配给 never
对象类型会进行属性合并
interface A {
name: string;
}
type B = { age: number };
let c: A & B = { name: "yyy", age: 100 };
两个类型有相同属性,其类型不同时,返回类型 never
let a = { name: "zhangsan" };
let b = { name: 12, age: 12 };
// 等价于 type HH = never
type HH = typeof a.name & typeof b.name;
let c = { name: "lisi", age: 12 }; // 报错 不能将string 分配给 never
为了解决上面的问题,我们可以使用 Pick 类型工具移除属性
let a = { name: "zhangsan" };
let b = { name: 12, age: 12 };
// 通过Pick 类型工具 移除属性
type HH = typeof a.name & Pick<typeof b, "age">;
let c = { name: "lisi", age: 12 };
函数
Function 首字母大写,这里的类型声明与 string、number、boolean 是有区别的
// 经过类型推导 a:Function
let aa = () => "aa";
参数类型
- 限定函数参数类型
function sum(a: number, b: number) {
return a + b;
}
console.log(sum(2, 3));
函数参数可选值
使用
?
修饰 表示可以选值- 参数 c 可以不变传递参数
- 不传参数时,值
undefined
function sum(a: number, b: number,c?:number) {
return a + b;
}
console.log(sum(2));
- 函数默认值
function sum(a: number, b: number, c: number = 10) {
return a + b + c;
}
返回值类型
自动类型推导 推导为 number
function sum(a: number, b: number) {
return a + b;
}
显示声明返回类型
function sum(a: number, b: number): string {
return a + b;
} // 函数被执行后 将返回string类型
- 当函数没有返回值时,建议使用 void 。TS 会自动推断
function log(text: string): void {
console.log(text);
}
参数声明
有时多个函数相同类型的参数,
let addUser = (user: { name: string; age: number }): void => {
console.log("添加用户");
};
let updateUser = (user: { name: string; age: number }): void => {
console.log("更新用户");
};
updateUser({ name: "张三", age: 18 });
这时我们可以采用 type 来进行类型声明,通过类型声明更好的复用类型
type userType = { name: string; age: number };
let addUser = (user: userType): void => {
console.log("添加用户");
};
let updateUser = (user: userType): void => {
console.log("更新用户");
};
updateUser({ name: "张三", age: 18 });
函数定义
- 无返回值
let log: (text: sting) => void;
- 有返回值
let sum: (a: number, b: number) => number;
sum = (x: number, y: number): number => x + y;
剩余参数
function sum(...args: number[]) {
return args.reduce((pre, cur) => pre + cur, 0);
}
Tuple 元组
元组类似于数组结构,元组要为每个值进行类型声明
const aa: [string, number] = ["hh", 18];
// 是一种严格约束
aa[0] = true; // 将会报错,第一个值必须是字符串类型
如果 定义数值只想定义值类型,不需要明确那个位置必须是什么值类型
const arr: (number | string | boolean)[] = ["张三", "今年", 18, "了"];
arr[arr.length] = "。"; // 我们来给它添加一个句号也是可以用的
函数重载
传递参数数量或类型不同 会有不同的结果
签名可以有多个 实现对应的只有一个
function sum(a: number, b: number): number;
function sum(a: string, b: string):string;
实现签名
实现签名是是函数功能的实现,对参数与返回值要包扩符合函数签名的宽泛类型。
- 重载签名可以是多个,实现签名只能是一个
- 实现签名是最终执行的函数
- 用户在调用时调用的是重载签名
- 重载签名可被调用,实现签名不能被调用
- 实现签名要使用通用类型
//重载签名
function getId(id: string): string;
function getId(id: number): number;
//实现签名
function getId(id: unknown): unknown {
if (typeof id === "string") {
return id;
}
return id;
}
//function getId(id: string): string (+1 overload)
getId("张三");
实现签名要使用通用的类型
function getId(id: string): string;
function getId(id: number): number;
//报错:因为实现签名不通用 「不能将类型“unknown”分配给类型“string”」
function getId(id: unknown): string {
if (typeof id === "string") {
return id;
}
return id;
}
getId("张三");