Skip to content

基础类型

上次更新 2024年10月11日星期五 2:18:42 字数 0 字 时长 0 分钟

js 弱类型语言,类型可以随意地去改变,得益于该特点让 js 代码从书写层面变得更灵活;

由于弱类型语言 缺乏类型约束,带来了诸多不便。

✅ ​ 举个例子

JavaScript
js
function getDes(name: "张三" | "王五") {
  return name === "张三" ? "人家永远18岁" : "我已年过半百的小老头";
}
getDes("张三"); // '人家永远18岁'
getDes("赵四"); // 输出却是:’我已年过半百的小老头‘ 不报错
typescript
typescript
function getDes(name: "张三" | "王五") {
  return name === "张三" ? "人家永远18岁" : "我已年过半百的小老头";
}
getDes("张三"); // '人家永远18岁'
getDes("赵四"); // 输出却是:’我已年过半百的小老头‘ 报错

如果想让js代码与ts代码同样的逻辑,需要给js代码加入繁杂的判断逻辑 增加了心智负担,

同时还很有可能增加维护成本。

类型校验

下面代码在不使用类型校验时,函数传递字符串是可以被执行的

js
function sum(a, b) {
  return a + b;
}
sum("1", 2); // 结果为'12'

加上严格类型校验,代码在编译环节会提示报错

javascript
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 被设置为 123number 类型时将会报错

ts
let str = "123"; // let str:string
str = 123; //报错 Type 'number' is not assignable to type 'string'.

数值

ts 中的数值类型包括了小数、负数、整数、(浮点数值 NaN),会被推断为 number 类型

ts
let num = 12; // let num:number
num = -100.1;
num = 101;
num = -Infinity;
num = NaN;

布尔值

值为 truefalse 会被推断为 boolean 类型

javascript
let bool = true; // let bool:boolean
bool = false;

数组

下面的类型推断结果,表示数组值为字符串

ts
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'.

下面的类型推到结果,表示数组值为数字

javascript
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'.

对象

对象的字面量推导

ts
const obj = { name: "true", sex: "nan", state: true };
// 推导结果如下
// const obj: {
//     name: string,
//     sex: string,
//     state: boolean
// }

如果向对象中添加类型中不存在的属性将会报错

js
const user = { name: "zhangsan", age: 18, open: true };

user.city = "北京";
//将产生错误 Property 'city' does not exist on type

下面是更复杂的在对象中嵌套对象,TS 也是可以推断出来的

js
const user = {
  name: "zhangsan",
  age: 18,
  open: true,
  lessons: [{ title: "JS" }, { title: "TS" }],
};

user.lessons[0].title = "javascript";

上例推断的结果是

js
const user: {
    name: string;
    age: number;
    open: boolean;
    lessons: {
        title: string;
    }[];
}

配置文件

TS 支持自定义配置项

初始化

执行以下命令创建配置项

shell
tsc --init # 初始化 tsconfig.json

然后执行命令使用自定义配置项进行监测

shell
tsc -w

配置选项

配置说明
noImplicitAny禁止使用隐含的 any 类型,如函数参数没有设置具体类型
strictNullChecks开启时不允许将 null、undefined 赋值给其他类型比如字符串
target转换成 JS 的版本
strict是否严格模式执行
module使用的模块系统

基本类型

除了上面的类型自动推导外,更多的时候是设置明确的类型

字符串

使用 string 来声明字符串类型

ts
let str: string = "123";

布尔

使用 boolean 来声明布尔类型

ts
let bool: boolean = true;

数字

在 TS 中不区分浮点和整数,都采用 number 来声明

ts
let num: number = 123;

数组

ts
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] //使用泛型来声明

元组

明确数组每个成员值类型的数组为元组

ts
let tuple: [string, number] = ["1", 1]; //明确每个成员值类型的数组

对象

限制类型为对象

ts
let obj: object = { name: "123", age: 12 }; //限制类型为对象

限制对象的值的类型

js
let obj2: { name: string, age: number } = { name: "123", age: 12 };

属性后面跟 ?. 用来指定参数可选,这样的属性参数为非必填项

js
let obj3: { name: string, age: number, happy?: string } = {
  name: "sss",
  age: 23,
}; // 属性值后面加? 表示可选属性

索引签名

如果有明确的索引名称

ts
// 如果有明确的索引名称
type HH = { name: string; age: number };
let obj: HH = { name: "123", age: 12 };

如果没有明确的索引名称

ts
// 如果没有明确的索引名称
type HH = { [key: string]: string | number; state: boolean };
let obj: HH = { name: "123", state: true };

带有后缀的索引

ts
//  带有后缀的索引
type HH = { [key: string + HX]: string | number }};
let obj: HH = { nameHX: "123" };
ts
//  或者使用模板字符串 带有后缀的索引
type HH = { [key: `${string}HX`]: string | number }};
let obj: HH = { nameHX: "123" };

使用 Record 工具类型来定义

ts
// 使用Record工具类型来定义
type HH = Record<string, number | string>; // key为string 值为 number|string
let hd: HH = { name: "name", age: 12 };

使用 Record 声明联合类型

ts
// 使用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 类型检测;
ts
let anyy: any = [];

// 以下代码均可正常编译
anyy = {};
anyy = "as";
anyy = 12;
anyy = true;
anyy = () => {};

unknown

unknown 类型是所有类型的子类型,所以 unknown 类型的变量可以赋值给其他类;

  • 任何类型都可以赋值给 unknown 类型;
  • unknown 类型的变量不能赋值给其他类;
  • 使用 unknown 类型可以进行类型检查;
  • 使用 unknown 类型可以进行类型断言;
any 与 unknown 区别 as 类型断言
ts
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 是不知道是什么类型 既不也不

ts
let pp: any = "123";
let ps: unknown = "1234444";
//正确
let xml: string = pp; //any
//报错
let xml1: string = ps; //unknown

null & undefined

null 与 undefined 也属于变量类型

ts
let aa: null = null;
let bb: underfined = undefined;
console.log(aa, bb);

void

  • 非严格模式下,void 类型的变量可以赋值 null | undefined;
  • 严格模式下,void 类型的变量只能赋值 undefined;
ts
let aa: void = null; //严格模式下会报错
let bb: void = undefined;

void 不允许设置为其他类型

ts
let aa: void = undefined;
aa = "str"; //将会报错

TS 中函数无任何返回内容,实际上返回的是 undefined

ts
function foo:void(){
    let str = 'str'
}
let fooPack = foo()
console.log(fooPack === undefined)

never

never 是任何类型的子类型 可赋值给任意类型 ,没有 类型是 never 的子类型

never 类型特点

  • never 没有任何子类型 所以 never 类型的变量不能赋值给其他类型变量
  • 函数抛出异常无限循环 返回 never
  • never 类型没有任何的值
  • never 类型是所有类型的子类
ts
type HH = never extends string ? string : boolean; //string
type HH = string extends never ? string : boolean; //boolean

union 联合类型

union 联合类型多个类型的组合,使用 | 来连接

ts
let hh: string | number = 12;
let arr: Array<string | number> = [12, "123"];

交叉类型

交叉类型是将 interface 、object 等进行组合,组合出新的类型

  • 交叉类型 要保证类型的一致性,string 与 number 交叉将 得到 never 类型
  • interface 、object 进行属性合并

类型交叉

ts
type HH = { age: number };
type HB = { name: string; age: string }; // 等同于 number string的交叉
let jj: HH & HB = { age: "123", name: "123" }; // 报错 不能将类型string分配给 never

对象类型会进行属性合并

ts
interface A {
  name: string;
}
type B = { age: number };

let c: A & B = { name: "yyy", age: 100 };

两个类型有相同属性,其类型不同时,返回类型 never

ts
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 类型工具移除属性

ts
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 是有区别的

ts
// 经过类型推导 a:Function
let aa = () => "aa";

参数类型

  • 限定函数参数类型
ts
function sum(a: number, b: number) {
  return a + b;
}

console.log(sum(2, 3));
  • 函数参数可选值

    使用? 修饰 表示可以选值

    • 参数 c 可以不变传递参数
    • 不传参数时,值undefined
ts
function sum(a: number, b: numberc?:number) {
    return a + b;
}
console.log(sum(2));
  • 函数默认值
ts
function sum(a: number, b: number, c: number = 10) {
  return a + b + c;
}

返回值类型

自动类型推导 推导为 number

ts
function sum(a: number, b: number) {
  return a + b;
}

显示声明返回类型

ts
function sum(a: number, b: number): string {
  return a + b;
} // 函数被执行后 将返回string类型
  • 当函数没有返回值时,建议使用 void 。TS 会自动推断
ts
function log(text: string): void {
  console.log(text);
}

参数声明

有时多个函数相同类型的参数,

ts
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 来进行类型声明,通过类型声明更好的复用类型

ts
type userType = { name: string; age: number };

let addUser = (user: userType): void => {
  console.log("添加用户");
};

let updateUser = (user: userType): void => {
  console.log("更新用户");
};

updateUser({ name: "张三", age: 18 });

函数定义

  • 无返回值
ts
let log: (text: sting) => void;
  • 有返回值
ts
let sum: (a: number, b: number) => number;
sum = (x: number, y: number): number => x + y;

剩余参数

ts
function sum(...args: number[]) {
  return args.reduce((pre, cur) => pre + cur, 0);
}

Tuple 元组

元组类似于数组结构,元组要为每个值进行类型声明

ts
const aa: [string, number] = ["hh", 18];

// 是一种严格约束
aa[0] = true; // 将会报错,第一个值必须是字符串类型

如果 定义数值只想定义值类型,不需要明确那个位置必须是什么值类型

ts
const arr: (number | string | boolean)[] = ["张三", "今年", 18, "了"];
arr[arr.length] = "。"; // 我们来给它添加一个句号也是可以用的

函数重载

传递参数数量或类型不同 会有不同的结果

签名可以有多个 实现对应的只有一个

ts
function sum(a: number, b: number): number;
function sum(a: string, b: string):string

实现签名

实现签名是是函数功能的实现,对参数与返回值要包扩符合函数签名的宽泛类型。

  • 重载签名可以是多个,实现签名只能是一个
  • 实现签名是最终执行的函数
  • 用户在调用时调用的是重载签名
  • 重载签名可被调用,实现签名不能被调用
  • 实现签名要使用通用类型
typescript
//重载签名
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("张三");

实现签名要使用通用的类型

typescript
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("张三");