网站托管维护方案,北京外包公司 网站开发,盐城seo营销,wordpress 用户前端前言
在 JavaScript 中#xff0c;变量是没有类型的#xff0c;变量的值的类型是在运行时确定的#xff0c;这被称为动态类型。 这意味着可以在不同的时间将不同类型的值赋给同一个变量#xff0c;并且 JavaScript 会在运行时根据当前赋给变量的值来确定其类型。 示例变量是没有类型的变量的值的类型是在运行时确定的这被称为动态类型。 这意味着可以在不同的时间将不同类型的值赋给同一个变量并且 JavaScript 会在运行时根据当前赋给变量的值来确定其类型。 示例
let a; // 声明一个变量 a
a 10; // 此时 a 的类型被确定为数字类型
a hello JavaScript; // 现在 a 的类型变为字符串类型a 的类型随着每次赋值而改变。 JavaScript 会在运行时根据实际赋给变量的值来自动调整和处理其类型。这种动态类型的特性使得 JavaScript 非常灵活但也可能导致一些难以察觉的错误因为类型的变化可能会在不经意间发生。
例如想要一个变量是数字并进行数学运算但它在运行时被意外地赋值为字符串就会出错。
let a 10;
let b 5;
console.log(a b); // 15
// 假如经历了一系列逻辑操作需要重新对 b 赋值 并计算结果
b 5;
console.log(a b); // 105假如在别的地方要使用这个结果会导致难以排查的错误。JavaScript是动态类型语言对类型的检查和约束相对较弱。
TypeScript 是 JavaScript 的超集它支持与JavaScript几乎相同的数据类型在 JavaScript 的基础上增加了静态类型系统。这意味着在 TypeScript 中开发者需要在编写代码时可以选择添加类型注释来明确指定变量、函数参数、函数返回值等的类型。
注意TypeScript 的类型注释将始终位于被输入的内容之后。
基础类型
string字符串类型。
// 声明一个变量 str同时指定 str 的类型为 string
let str: string;
// str 的类型设置为 string从此以后str 的值只能是 字符串类型
str Hello; // 字符串类型
str 123; // 编译错误: Type number is not assignable to type string.在编写代码时编辑器报错不能将类型“number”分配给类型“string”。 因为变量str的类型是string。
可以使用模版字符串
let name: string 张三;
let desc: string 我的名字是 ${name};number数字类型。
数字类型不分int 和 float全是number。
let decLiteral: number 6;
let hexLiteral: number 0xf00d;
let binaryLiteral: number 0b1010;
let octalLiteral: number 0o744;除了支持十进制和十六进制字面量TypeScript还支持ECMAScript 2015中引入的二进制和八进制字面量。
boolean布尔类型。
布尔类型只有两个值true/ false。
let isDone: boolean false;始终使用string、number或boolean来表示类型。
数组类型
TypeScript有两种方式可以定义数组。
在基础数据类型后面接上 []表示由此类型元素组成的一个数组
let list1: number[] [1, 2, 3]; // 数字数组数组的每个元素都是number类型
let list2: string[] [list1, list2]; // 字符串数组数组的每个元素都是string类型
let list3: boolean[] [true, false, true]; // 布尔数组数组的每个元素都是boolean类型let arr: string[][] [[a, b], [c, d]]; // 声明一个二维字符串数组。使用数组泛型Array元素类型
let list1: Arraynumber [1, 2, 3];
let list2: Arraystring [list1, list2];
let list3: Arrayboolean [true, false, true];元组 Tuple
TypeScript 中的元组Tuple是一种特殊的数组类型。 元组类型允许表示一个已知元素数量和类型的数组各元素的类型不必相同。
特点
固定的元素数量和类型顺序 元组中的元素数量是明确的并且每个位置的元素类型是固定的。 混合类型 可以在一个元组中包含不同类型的元素。
示例
// 定义一个包含字符串、数字和布尔值的元组
let myTuple: [string, number] [Hello, 42];
// 语句没有报错why?
myTuple.push(为什么这句不报错);
// 访问元组中的元素通过索引
console.log(myTuple[0]); // Hello
console.log(myTuple[1]); // 42
// 错误Type string is not assignable to type number.(不能将类型“string”分配给类型“number”。)
myTuple[1] not a number; // 编译错误Type string is not assignable to type number.// 错误Type true is not assignable to type undefined.(不能将类型“true”分配给类型“undefined”。)
// 错误Tuple type [string, number] of length 2 has no element at index 2.(长度为 2 的元组类型 [string, number] 在索引 2 处没有元素。)
myTuple[2] true; // 错误Argument of type boolean is not assignable to parameter of type string | number.(类型“boolean”的参数不能赋给类型“string | number”的参数。)
myTuple.push(true);// 语句没有报错why?
// myTuple.push(world); TypeScript 的元组和数组的区别
元组具有固定数量的元素且每个元素的类型是明确且固定的。元组对每个位置的元素类型有严格的约束。数组元素数量可以动态变化。如果指定了类型如 number[] 则要求所有元素都是指定的类型如果未指定类型元素类型可以不同any类型。
枚举
枚举Enum类型是对Java Script标准数据类型的一个补充。 枚举Enum是一种为一组数值赋予有意义名称的方式。 默认情况下从 0 开始为元素编号。
enum Color {Red, Green, Blue}
console.log(Color);
// 输出以下结果
// {
// 0: Red,
// 1: Green,
// 2: Blue,
// Red: 0,
// Green: 1,
// Blue: 2
// } 代码定义了一个名为 Color 的枚举类型其中 Red 被初始化为 0 Green 为 1 Blue 为 2 。 也可以手动为枚举成员赋值
enum Color1 {Red 11,Green 22,Blue 33
}
console.log(Color1);
// 输出
// {
// 11: Red,
// 22: Green,
// 33: Blue,
// Red: 11,
// Green: 22,
// Blue: 33
// } 枚举成员可以通过两种方式访问
let c: Color Color.Red; // 通过枚举名.成员名
console.log(c); // 输出 0默认情况let colorName Color[2]; // 通过枚举值获取枚举名
console.log(colorName); // 输出 Green默认情况Any
当一个变量被声明为 any 类型时它可以被赋予任何类型的值并且在对其进行操作时TypeScript 编译器不会进行类型检查。 any 完全放弃了类型检查。
// 显式声明变量为any类型
let variable: any 5;
variable Hello;
variable true; let list: any[] [1, true, free]; // list 包含了不同的类型的数据
list[1] 66;// 隐式声明变量为any类型
// 声明变量 并且 不指定类型 TS 解析器会自动判断变量的类型为any
let variable1;
variable1 Hello;
variable1 true; 在上述代码中variable 、variable1 可以被随意赋值为不同类型的值。
any 类型的变量还可以赋值给任意类型的变量
let variable: any 5;
variable Hello;
variable true; let str : string;
str variable;
console.log(str); // true; TS类型检查器不会再检查 变量 str 。通常只有在确实无法确定变量类型或者需要与旧的 JavaScript 代码进行交互且类型难以明确时才建议使用 any 类型。
unknown
unknown 类型表示一个值的类型是未知的。 只有 any 类型和 unknown 类型的值可以赋给 unknown 类型的变量。 unknown 类型是一种比 any 更安全的类型对 unknown 类型的值进行操作是受到限制的。 例如如果有一个变量 x 的类型是 unknown 在对其进行操作之前必须先进行类型断言或类型缩小的检查
let x: unknown;
// 错误不能直接对 unknown 类型的值进行操作
// x.toUpperCase(); // 报错x 的类型是 “未知”
if (typeof x string) {// 经过类型缩小检查后可以进行操作x.toUpperCase();
}可以对 x 进行任意赋值
let variable: any;
let x: unknown;
x Hello; // 合法因为可以将 any 或 unknown 类型的值赋给 unknown 类型的变量
x true;
x variable; // 合法因为可以将 any 或 unknown 类型的值赋给 unknown 类型的变量不能将 unknown 类型的变量 赋值给 除any类型外的 其他类型的变量
let str : string;
str x; // 不能将类型“unknown”分配给类型“string”。let variable: any;
variable x; // 合法因为 any 类型可以被赋值任意类型的值unknown 类型的主要用途是在处理可能来自不可信或动态来源的数据时提供一种更严格的类型控制以防止意外的类型错误。
unknown 和 any 的区别
类型安全性 any 完全放弃了类型检查可以对 any 类型的变量执行任何操作而 TypeScript 编译器不会产生错误。unknown 则更安全对 unknown 类型的变量进行操作时TypeScript 编译器会要求先进行类型断言或类型缩小检查以确保操作的合法性。 可赋值性 任何类型的值都可以赋给 any 类型的变量。 any 类型的变量可以被赋值给任意类型的变量并关闭该变量的TypeScript 静态类型检查。只有 any 类型和 unknown 类型的值可以赋给 unknown 类型的变量。 unknown 类型的变量不可以被赋值给 除 any 类型外 的 其他类型的变量。 操作限制 对于 any 类型的变量可以直接调用任何方法和访问任何属性而无需考虑其实际类型。对于 unknown 类型的变量在没有进行类型断言或缩小检查之前不能直接调用方法或访问属性。
Void
在 TypeScript 中void 类型用于表示没有任何返回值的函数。 当变量被声明为 void 类型时只能被赋值为 undefined :
let unusable: void undefined;
let unusable1: void null; // 报错不能将类型“null”分配给类型“void”。void 类型用来表示函数没有返回值的情况。当你尝试将 null 赋值给一个 void 类型的变量时编译器会报错因为 null 表示对象的引用而 void 不是一个对象它只是一个表示无返回值的特殊类型。 当一个函数没有返回值时你通常会见到其返回值类型是 void
function printMsg(): void {console.log(This is my message);
}function noReturn(): void {return // return undefined
}Null 和 Undefined
在 TypeScript 中null的类型是null undefined 的类型是undefined 。
默认情况下null和undefined是所有类型的子类型。 就是说你可以把 null和undefined赋值给其他类型的变量。
let u: undefined undefined; // 变量类型是undefined值是undefined
let n: null null; // 变量类型是null 值是null let num: number null; // 合法但不推荐
let str: string;
console.log(str); // 输出 undefined在 TypeScript 的严格空值检查模式通过在 tsconfig.json 中设置 strictNullChecks: true 下对 null 和 undefined 的处理更加严格。例如函数参数不能默认是 null 或 undefined 除非明确指定类型为 number | null 或 string | undefined 这样的联合类型。
文中所有例子都假设strictNullChecks: false。
Never
never类型表示的是那些永不存在的值的类型。
在 TypeScript 中一般不会直接创建 never 类型的变量。因为 never 表示永远不会有值的类型。 但在某些复杂的类型推导或函数的返回类型中可能会出现被推断为 never 类型的情况。
它通常用于以下几种情况
函数中抛出异常或进入一个无限循环
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {throw new Error(message);
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {while (true) {}
}// 推断的返回值类型为never
function fail() {return Error(Something failed);
}
用来表示一个函数的返回值类型当这个函数永远不会正常返回即总是以抛出异常结束。
例如如果一个函数用于处理所有可能的情况并且对于某些输入它应该抛出错误那么其返回类型可以是 never
function exhaustiveCheck(x: string): never {if (x a) {// 一些处理逻辑} else if (x b) {// 一些处理逻辑} else {throw new Error(Unexpected value);}
}object
在 TypeScript 中object 类型用于表示非原始类型除 numberstringbooleansymbolnull或undefined 外的类型。
let obj: Object;
obj { name: John }; // obj 是一个对象
obj function() {}; // obj是一个函数函数也是对象let arr: Object [1, 2, 3]; // 数组是特殊的对象在JavaScript中几乎所有的数据类型都可以被视为对象或者具有对象的某些特性。
基本数据类型如数字、字符串、布尔值在 JavaScript 中都有对应的对象形式。像数字 5 对应的对象形式是 new Number(5) 字符串 hello 对应的对象形式是 new String(hello) 布尔值 true 对应的对象形式是 new Boolean(true) 。 函数也是对象它们可以具有属性和方法。 数组也是一种特殊的对象。 甚至一些内置的全局对象如 Math 、 Date 等也提供了各种功能和方法。 示例
function myFunction() {console.log(Hello!);
}
myFunction.property Some value; // 函数可以添加属性let num 5;
let numObj new Number(num); // 将数字转换为对象形式在 TypeScript 中直接声明一个变量的类型为 object 相对较少使用并且通常不是最佳实践。 只是声明变量的类型为 object 它并不能明确对象应该具有哪些具体的属性可能会导致类型检查不够严格和代码的可读性降低。 object 类型通常不是用于简单地判断一个变量是不是一个普通的对象而是更侧重于描述对象所具有的特定属性结构。 大多数情况下object 被用来限制对象中的属性而不是限制变量的类型是不是一个对象。
// 声明一个对象类型的变量 obj 并且指定其属性 name 的类型为字符串
let obj: {name: string};
obj {}; // Error: Property name is missing in type {} but required in type { name: string; }.
// (类型 {} 中缺少属性 name但类型 { name: string; } 中需要该属性。)// 对象obj的赋值必须和obj声明的结构一模一样
obj {name: 张三};
obj {name: 张三, age: 18}; // Error: Object literal may only specify known properties, and age does not exist in type { name: string; }.
// (对象字面量只能指定已知属性并且“age”不在类型“{ name: string; }”中。)属性名后面加上?表示是可选属性
// 这里的 ? 表示 age 属性是可选的
let obj: { name: string, age?: number };
obj {name: 张三}; // 合法, 因为 age 是可选属性
obj {name: 张三, age: 18}; // 合法假设对象obj必须要有name属性其余属性可有可无
let obj: { name: string, [propName: string]:any };
obj {name: 张三, age: 18}; // 合法
obj {name: 张三, age: 18, sex: male}; // 合法// Error: Property name is missing in type { age: number; } but required in type { [propName: string]: any; name: string; }.
obj {age: 18}; // 不合法因为没有 name 属性// Error: Type number is not assignable to type string.
obj {name: 18}; // 因为 name 属性不是字符串类型let obj 声明了一个变量 obj 。{ name: string, [propName: string]:any } 定义了这个变量的类型。 name: string 明确指定了 obj 必须具有一个名为 name 的属性且这个属性的值必须是字符串类型。[propName: string]:any 这部分是一个索引签名。propName 是一个变量名在这里只是表示任意属性的名称。string 表示属性名的类型是字符串。any 表示对应属性的值可以是任何类型。 这意味着除了必须存在的 name 属性是字符串类型外obj 还可以有任意数量的其他属性这些属性的名称是字符串并且值可以是任何类型。
使用object类型可以更好的表示像Object.create这样的API。
declare function create(o: object | null): void;declare 关键字用于声明存在一个在其他地方定义可能是在外部模块、全局环境或其他源文件的函数。 这段代码声明了一个名为 create 的函数它接受一个参数 oo 的类型可以是一个对象或者 null 并且这个函数没有返回值void。 调用create 函数
create({ key: value });
create(null); create(5); // 错误因为 5 不是对象也不是 null
create(string); // 错误因为 string 不是对象也不是 null联合类型声明
基本用法
let value: string | number; // 表示 value 可以是字符串或者数字。
value hello typescript; // 合法
value 123; // 合法
value []; // 不合法函数参数
function printValue(value: string | number) {if (typeof value string) {console.log(Its a string: ${value});} else {console.log(Its a number: ${value});}
}printValue(World); // 合法
printValue(456); // 合法与对象类型相结合
interface Person {name: string;age: number;
}interface Animal {species: string;
}let entity: Person | Animal;entity { name: John, age: 30 }; // 合法
entity { species: Dog }; // 合法
entity { name: John, age: 30, species: Dog }; // 合法声明了一个变量 entity 其类型为 Person 或 Animal 的联合类型这意味着 entity 可以被赋值为符合 Person 接口结构的对象或者符合 Animal 接口结构的对象也可以同时符合 Person 和 Animal 结构的对象。 通过联合类型的声明使得 entity 变量能够灵活地接受不同类型但具有特定结构的对象值。
数组中的联合声明
let arr: (string | number)[];
arr [Hello, 123, World, 456]; // 合法arr [Hello, 123, World, true]; // 不合法
// Error: Type boolean is not assignable to type string | number.(string | number)[] 是数组的类型声明。其中 string | number 是联合类型表示数组中的元素可以是字符串类型string或者数字类型number。
联合类型为处理可能具有多种类型的值提供了灵活性。
交叉类型声明
在 TypeScript 中交叉类型Intersection Type用于将多个类型合并为一个新的类型该新类型具有所合并类型的所有成员。 交叉类型使用 符号来创建。 例如如果有两个接口 User 和 Employee
interface User {name: string;age: number;sayYes(): void;
}interface Employee {jobTitle: string;salary: number;sayNo(): void;
}创建一个交叉类型 UserEmployee
type UserEmployee User Employee;// 错误 “UserEmployee”仅表示类型但在此处却作为值使用。
console.log(UserEmployee ); // Error: UserEmployee only refers to a type, but is being used as a value here.UserEmployee 类型的对象同时具有 User 和 Employee 接口中定义的所有属性和方法
let person: UserEmployee {name: 张三,age: 30,jobTitle: Developer,salary: 10000,sayYes() {console.log(sayYes!);},sayNo() {console.log(sayNo!);}
};在交叉类型中需要注意以下几点
同名属性的类型必须兼容。如果两个接口中都有一个名为 id 的属性但一个是字符串类型另一个是数字类型就会导致类型错误。方法也会被合并如果两个接口中有同名的方法那么它们的实现需要保持一致否则也会导致类型错误。交叉类型可以用于多个接口、类型别名甚至具体的对象类型的合并。
交叉类型在一些场景中非常有用比如需要一个对象同时满足多个不同的类型约束或者需要将多个相关但又有所不同的类型组合在一起。