这是我参与「第五届青训营 」笔记创作活动的第4天
1.TS基本语法学习
1.1基础数据类型
- number:表示数值,可以是整数或浮点数。
let x: number = 10;
- string:表示字符串。
let name: string = 'John Doe';
- boolean:表示布尔值,只能是 true 或 false。
let isValid: boolean = true;
- void:表示没有任何类型。
function print(): void {
console.log("Hello World!");
}
- any:表示任意类型。
let data: any = 'Hello World!';
data = 10;
1.2对象数据类型
其中IBytedancer表示一个类型,类型必须以大写I开头
- readonly:表示只读属性,一旦被赋值就不能再次赋值。使用 readonly 修饰符可以将属性设置为只读,只能在初始化时赋值,后续不能再修改。
- 可选属性:在属性名后面加 ? 表示该属性可选,可以不赋值。
- 任意属性: 通过使用 [propertyName: string]: any; 表示任意属性来扩展对象的类型,使得对象可以有任意属性。
1.3函数类型
1.3.1函数类型明确
JS函数代码可以用TS代码来修改和标注,在 JavaScript 中,函数类型通常是隐式的,可以通过函数参数和返回值来推断函数类型。在 TypeScript 中,可以使用类型注释来明确函数类型。
下面是 JavaScript 中一个简单函数的例子:
function add(a, b) {
return a + b;
}
let result = add(1, 2);
console.log(result); // 3
下面是上面这个函数的 TypeScript 版本:
function add(a: number, b: number): number {
return a + b;
}
let result = add(1, 2);
console.log(result); // 3
在TypeScript中,我们可以在函数的参数和返回值前面加上类型注释,来明确函数的类型。在这个例子中,我们给函数的参数 a 和 b 都加上了 number 类型的注释,并且给函数的返回值加上了 number 类型的注释。
这样做的好处是,编译器可以通过这些类型注释来进行类型检查,确保函数的调用是正确的。
1.3.2函数重载
TypeScript 中的函数重载允许我们定义多个相同名称的函数,但是它们必须有不同的参数类型和数量。当调用重载函数时,TypeScript 会根据传入的参数类型和数量来自动选择正确的函数版本。
举个例子,下面是一个重载函数的例子:
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
return a + b;
}
console.log(add(1, 2)); // 3
console.log(add("Hello, ", "world!")); // "Hello, world!"
在这个例子中,我们定义了两个函数重载版本,一个接受两个数字参数并返回一个数字,另一个接受两个字符串参数并返回一个字符串。之后我们定义了一个同名的实现函数,该函数接受两个 any 类型的参数并返回一个 any 类型的值,这个函数是重载的实际实现。
当我们调用 add(1, 2) 时,编译器会根据参数类型来选择第一个重载版本,而当我们调用 add("Hello, ", "world!") 时,编译器会选择第二个重载版本。
通过重载函数可以提高程序的可读性和可维护性,让函数更加严谨,避免了传入错误类型的参数导致的错误。
1.4数组类型
在 TypeScript 中,数组类型可以使用多种表示方法。
- 使用 Array 类型表示泛型数组。这种方式可以表示任意类型的数组。
let numbers: Array<number> = [1, 2, 3, 4, 5];
- 使用 T[] 类型表示数组,等价于 Array。这种方式可以表示任意类型的数组。
let numbers: number[] = [1, 2, 3, 4, 5];
- 使用元组类型表示数组,可以表示固定类型的数组。
let person: [string, number] = ['Tom', 25];
- 使用接口表示数组,可以表示任意类型的数组。
interface NumberArray {
[index: number]: number;
}
let numbers: NumberArray = [1, 2, 3, 4, 5];
总结:
-
TypeScript 中可以使用 Array,T[],元组类型或接口表示数组类型。
-
Array 和 T[] 是最常用的两种表示方式,可以表示任意类型的数组。
-
元组类型可以表示固定类型的数组,适用于需要存储固定类型且顺序固定的数据的场景。
-
接口可以表示任意类型的数组,可以灵活地定义数组的类型和结构。
1.5补充类型
1.6泛型
TypeScript 的泛型是一种类型参数化的设计,可以帮助我们创建可重用的组件和函数。泛型允许我们在定义函数、接口和类时使用类型参数,以便在运行时确定具体类型。
举个例子,我们可以定义一个名为 identity 的函数,该函数接受任意类型的参数并返回相同的类型。
function identity<T>(arg: T): T {
return arg;
}
我们可以使用泛型的方式来调用这个函数,并传入具体类型参数
let output = identity<string>("myString"); // type of output will be 'string'
console.log(output); // myString
此外,我们可以使用类型推断来简化调用过程,让编译器自动推断出类型
let output = identity("myString"); // type of output will be 'string'
console.log(output); // myString
泛型还可以用于类和接口上,例如:
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
总结:
- 泛型是 TypeScript 中帮助我们构建可重用组件和函数的重要概念
- 泛型允许我们在定义函数、接口和类时使用类型参数,以便在运行时确定具体类型。
- 泛型函数可以接受任意类型的参数并返回相同的类型。
- 可以使用类型推断来简化调用过程,让编译器自动推断出类型
- 泛型还可以用于类和接口上,帮助我们构建更加灵活和类型安全的组件
1.7interface用法
在 TypeScript 中,interface 是一种强类型的声明,用于定义一组属性和行为的模板。它可以用于对象、函数、类等的类型检查和限制。
语法:
Copy code
interface 接口名称 {
属性1: 类型1;
属性2: 类型2;
……
方法1(): 返回类型1;
方法2(): 返回类型2;
……
}
例如:
interface Point {
x: number;
y: number;
}
let point: Point = { x: 1, y: 2 };
console.log(point);
在上面的例子中,我们定义了一个名为 Point 的接口,它包含了两个属性 x 和 y,它们的类型都是 number。然后我们定义了一个变量 point,它的类型是 Point,这意味着它必须包含 x 和 y 两个属性,且它们的类型都是 number。
可以使用 interface 来定义函数参数类型和函数返回类型
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
总结:
- interface 是 TypeScript 中一种强类型的声明,用于定义一组属性和行为的模板。
- 可以用于对象、函数、类等的类型检查和限制。
2.高级类型
2.1联合,交叉类型
TypeScript 中的联合类型(Union Types)和交叉类型(Intersection Types)都是用来组合多个类型的一种语法。
- 联合类型(Union Types): 联合类型表示一个值可以是几种类型中的一种,用"|"来连接多种类型。
let unionType: string | number;
unionType = "hello";
console.log(unionType); // hello
unionType = 123;
console.log(unionType); // 123
- 交叉类型(Intersection Types): 交叉类型表示一个值同时拥有多种类型的特性,用"&"来连接多种类型。
interface A {
a: string
}
interface B {
b: number
}
let intersectionType: A & B;
intersectionType = { a: "hello", b: 123 };
console.log(intersectionType.a); // hello
console.log(intersectionType.b); // 123
- TypeScript 中的联合类型(Union Types)和交叉类型(Intersection Types)都是用来组合多个类型的一种语法。
- 联合类型表示一个值可以是几种类型中的一种,用 "|" 来连接多种类型。
- 交叉类型表示一个值同时拥有多种类型的特性,用 "&" 来连接多种类型。
- 联合类型和交叉类型可以帮助我们更好地描述变量的类型,提高代码的类型安全性和可读性。
2.2类型守卫
TypeScript 提供了类型守卫(Type Guards)功能,可以帮助我们在运行时动态检查变量的类型。类型守卫可以使用 typeof、instanceof 和 in 运算符来实现。
- typeof 类型守卫: 利用 JavaScript 的 typeof 运算符来判断变量的类型。
function isString(value: any): value is string {
return typeof value === 'string';
}
let input: any = 'hello';
if (isString(input)) {
console.log(input.length);
} else {
console.log(input);
}
- instanceof 类型守卫: 利用 JavaScript 的 instanceof 运算符来判断变量是否是某个类的实例。
class Bird {
fly(): void {
console.log("I am flying");
}
layEggs(): void {
console.log("I am laying eggs");
}
}
class Fish {
swim(): void {
console.log("I am swimming");
}
layEggs(): void {
console.log("I am laying eggs");
}
}
function isFish(pet: Fish | Bird): pet is Fish {
return pet instanceof Fish;
}
let pet: Fish | Bird = new Fish();
if (isFish(pet)) {
pet.swim(); // I am swimming
} else {
pet.fly(); // I am flying
}
- in 类型守卫: 利用 JavaScript 的 in 运算符来判断对象是否包含某个属性。(这个不太会,就不放示例代码给大家了)
- 类型守卫是 TypeScript 中的一种功能,可以帮助我们在运行时动态检查变量的类型。
- 类型守卫可以使用 typeof、instanceof 和 in 运算符来实现。
- 类型守卫可以帮助我们提高代码的可读性和类型安全性。
2.1+2.2自动类型推断
TypeScript 中的联合类型(Union Types)和类型保护(Type Guards)可以合成自动类型推断。 这意味着,在使用联合类型和类型保护后,TypeScript 可以自动推断出某个变量在特定代码块中的类型。
function getType(val: string | number): string {
if (typeof val === 'string') {
return "string";
} else {
return "number";
}
}
let val: string | number = "hello";
console.log(getType(val)); // string
val = 123;
console.log(getType(val)); // number
在上面的代码中,函数 getType 接受一个参数 val,它的类型是联合类型(string | number)。在函数体内部,我们使用了类型保护(typeof)来检查 val 的类型。 当 val 的类型是 string 时,函数返回 "string",当 val 的类型是 number 时,函数返回 "number"。 由于我们使用了类型保护,TypeScript 就可以在这两种情况下自动推断出 val 的类型。在第一个 console.log 调用中,val 的类型是 string,在第二个 console.log 调用中,val 的类型是 number。
2.3
在 TypeScript 中,extends 关键字用于定义泛型约束。它可以限制泛型类型只能是指定的类型或者它的子类型。
语法:
function myFunction<T extends 类型>(arg: T): T {
return arg;
}
例如:
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity({length: 10, value: 3});
在上面的例子中,我们定义了一个接口 Lengthwise,它有一个 length 属性。然后我们使用泛型函数 loggingIdentity,它有一个类型参数 T 和一个参数 arg,我们在 T 上使用了 extends 关键字并将其限制为 Lengthwise 类型或其子类型,这样我们就可以在函数内部访问 arg.length 了。
而infer关键字主要用于在类型推断中提取出来类型进行操作。语法:
function myFunction<T, U extends Array<T>>(arg: U) {
let [first] = arg;
return first;
}
在上面的例子中,我们定义了一个泛型函数 myFunction,它有两个类型参数 T 和 U。使用 infer关键字提取出 U 类型的元素的类型,并将其赋值给 T。这样就可以在函数内部正确地使用 T 类型了。
总结:
- extends 关键字用于定义泛型约束,限制泛型类型只能是指定的类型或者它的子类型。
- infer 关键字主要用于在类型推断中提取出来类型进行操作。