【青训营】TypeScript入门

62 阅读10分钟

这是我参与「第五届青训营 」笔记创作活动的第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对象数据类型

image.png

其中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 中,数组类型可以使用多种表示方法。

  1. 使用 Array 类型表示泛型数组。这种方式可以表示任意类型的数组。
let numbers: Array<number> = [1, 2, 3, 4, 5];
  1. 使用 T[] 类型表示数组,等价于 Array。这种方式可以表示任意类型的数组。
let numbers: number[] = [1, 2, 3, 4, 5];
  1. 使用元组类型表示数组,可以表示固定类型的数组。
let person: [string, number] = ['Tom', 25];
  1. 使用接口表示数组,可以表示任意类型的数组。
interface NumberArray {
    [index: number]: number;
}
let numbers: NumberArray = [1, 2, 3, 4, 5];

总结:

  • TypeScript 中可以使用 Array,T[],元组类型或接口表示数组类型。

  • Array 和 T[] 是最常用的两种表示方式,可以表示任意类型的数组。

  • 元组类型可以表示固定类型的数组,适用于需要存储固定类型且顺序固定的数据的场景。

  • 接口可以表示任意类型的数组,可以灵活地定义数组的类型和结构。

1.5补充类型

image.png

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)都是用来组合多个类型的一种语法。

  1. 联合类型(Union Types): 联合类型表示一个值可以是几种类型中的一种,用"|"来连接多种类型。
let unionType: string | number;
unionType = "hello";
console.log(unionType); // hello
unionType = 123;
console.log(unionType); // 123
  1. 交叉类型(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 运算符来实现。

  1. 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);
}
  1. 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
}
  1. 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 关键字主要用于在类型推断中提取出来类型进行操作。