学习TypeScript -- 基础部分

1. 认识TS

什么是TS?

TS(TypeScript)是一种由Microsoft开发和维护的编程语言,它是JavaScript的超集,支持静态类型检查、类、接口、泛型等特性。TS最终会被编译为标准的JavaScript代码,因此可以运行在任何支持JavaScript的环境中。

使用TS带来了许多好处,例如更早地发现类型相关的错误、提高代码可读性、使用面向对象的编程范式等。同时,由于TS与JS具有相似的语法和语义,因此学习曲线相对较低,也很容易与现有的JavaScript项目进行集成。

image.png

TS 相关学习资源

TS开源教程以及应用

在线编译 TS => JS的代码演练场

2. TS 基础

TS里面的基础数据类型

在TypeScript中,基本数据类型包括:

  1. number:表示数字类型,包括整数和浮点数。例如:
let age: number = 18;
let price: number = 9.99;
  1. string:表示字符串类型。例如:
let name: string = "Alice";
let message: string = `Hello, ${name}!`;
  1. boolean:表示布尔类型,只有 truefalse 两个值。例如:
let isStudent: boolean = true;
let hasJob: boolean = false;
  1. nullundefined:表示空值和未定义的值。例如:
let emptyValue: null = null;
let notDefined: undefined = undefined;
  1. symbol:表示独一无二的值。例如:
let uniqueKey: symbol = Symbol('key');
let anotherKey: symbol = Symbol('key');
console.log(uniqueKey === anotherKey); // false
  • any AnyScript🫢😏

除此之外,还有一种特殊的类型叫做 any,它可以存储任意值,不会进行类型检查。例如:

let anything: any = 'hello';
anything = 42;
anything = true;

以上是常用的基础数据类型,需要注意的是,在 TypeScript 中,变量声明时可以省略数据类型,TypeScript 会根据变量的初始值自动推断出数据类型。例如:

let count = 10; // 推断为 number 类型
let message = "Hello!"; // 推断为 string 类型
let flag = true; // 推断为 boolean 类型

TS 里面的高级数据类型

除了上述基本数据类型外,TypeScript 还提供了以下几种高级数据类型:

1.Array:表示数组类型,可以存储一组相同类型的值。例如:

let numbers: number[] = [1, 2, 3];
let names: string[] = ["Alice", "Bob", "Charlie"];

也可以使用泛型来声明数组类型:

let scores: Array<number> = [80, 90, 95];

2.Tuple:表示元组类型,用于存储固定长度和类型的数组。例如:

let user: [string, number] = ["Alice", 18];

3.Enum:表示枚举类型,用于定义一组常量。例如:

enum Color {
  Red,
  Green,
  Blue
}

let color: Color = Color.Red;

4.Object:表示对象类型,用于存储键值对。例如:

let person: {name: string, age: number} = {
  name: "Alice",
  age: 18
};

5.Function:表示函数类型,包括参数类型和返回值类型。例如:

function add(x: number, y: number): number {
  return x + y;
}

6.Void:表示空类型,通常用于没有返回值函数。例如:

function sayHello(): void {
  console.log("Hello!");
}

7.Never:表示永远不存在的值的类型,通常用于抛出异常或者无限循环等操作。例如:

function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {}
}

详解TS 里面的函数类型

定义: TS定义函数类型时要定义输入参数类型和 输出类型

输入参数: 参数支持可选参数和默认参数

输出参数: 输出可以自动推断,没有返回值时,默认为void 类型

函数重载: 名称相同 但是参数不同, 可以通过重载支持多种类型

// 1.定义 参数以及类型 返回值以及类型 ❓ 表示可传可不传
let L = function (name: string, age: number, hoby?: string): string {
    return name + age
}
let L1 = L("张三", 20) // "张三20"

TS interface 接口

在 TypeScript 中,interface 是一种用于描述对象类型的语法,它定义了一个对象应该具有哪些属性和方法。接口可以被其他类型或者函数实现(implement) 或扩展(extend),让代码更加灵活和可读性强。

  • 可选属性 : ? 表示该属性在写入的时候可填可不填
  • 只读属性 : readonly 关键字表示该属性只可以读取,但不可以修改
  • 可以描述函数类型
  • 可以描述自定义属性

总结: 接口非常灵活 duck typing

以下是一个简单的 interface 示例:

interface Person {
  name: string; // name的属性值必须是string类型
  age: number; // age的属性值必须是number类型
}

let person: Person = {
  name: "Alice",
  age: 18
};

上面的代码定义了一个名为 Person 的接口,它有两个属性: nameage,分别表示人的姓名和年龄。使用 Person 接口来声明一个变量时,必须遵循 Person 接口的属性定义。

可以使用 extends 关键字来扩展接口,例如:

interface Employee extends Person {
  jobTitle: string;
}

let employee: Employee = {
  name: "Bob",
  age: 25,
  jobTitle: "Developer"
};

上面的代码定义了一个名为 Employee 的接口,它继承了 Person 接口,并新增了一个 jobTitle 属性。

另外,接口不仅可以用来描述对象类型,还可以用来描述函数类型。例如:

interface Add {
  (x: number, y: number): number;
}

let add: Add = function(x, y) {
  return x + y;
};

上面的代码定义了一个名为 Add 的接口,它描述了一个接受两个参数并返回一个数字类型的函数。使用 Add 接口来声明一个变量 add,并将一个函数赋值给它,该函数满足 Add 接口的定义。

综上所述,interface 是 TypeScript 中描述复杂对象和函数类型的重要语法之一,它可以提高代码的可读性和灵活性。

3. TS 进阶

类型操作符

在 TypeScript 中,有三种常见的类型操作符:交叉类型联合类型类型断言。

1. 交叉类型 &

交叉类型(Intersection Types)是将多个类型合并成一个类型,表示这个类型包含了多个类型的所有特性。使用 & 符号连接多个类型即可。例如:

interface Person {
  name: string;
}

interface Employee {
  jobTitle: string;
}

type PersonWithJob = Person & Employee;

let personWithJob: PersonWithJob = {
  name: "Alice",
  jobTitle: "Developer"
};

上面的代码中,定义了两个接口 PersonEmployee,然后使用交叉类型操作符 & 将它们合并为一个新的类型 PersonWithJob,表示既是 Person 又是 Employee 的对象。最后用 personWithJob 表示一个具有 PersonWithJob 类型的对象。

let num: number | string // num这个变量可以是number 也可以是 string 类型
num = 10; //ok
num = "10"  // ok

2. 联合类型 |

2.联合类型(Union Types)是指可以表示多个类型中的一种的类型。使用 | 符号连接多个类型即可。例如:

type NumberOrString = number | string;

let value1: NumberOrString = 123; // ok
let value2: NumberOrString = "abc"; // ok
let value3: NumberOrString = true; // error

上面的代码中,定义了一个联合类型 NumberOrString,表示可以是数字字符串类型。然后用 value1value2 分别赋值为数字和字符串,都是合法的。但是尝试将 value3 赋值为布尔值时,会得到一个类型错误。

3. 类型断言 as <>

3.类型断言(Type Assertion)是指在编译器无法确定类型时手动告诉编译器它的类型。可以使用尖括号 <> 语法或 as 关键字来进行类型断言。例如:

let value: any = "123";
let length1: number = (<string>value).length;
let length2: number = (value as string).length;

上面的代码中,定义了一个变量 value,它的类型为 any,即未知类型。然后使用类型断言获取 value 的长度,并将结果赋值给变量 length1length2。两种方式都可以实现类型转换,但是推荐使用 as 关键字的语法。

4. 类型别名

定义: 给类型起一个别名

相同点:

  • 都可以定义对象或者函数
  • 都允许继承

差异点:

  1. interface 是Ts用来定义对象, type 是用来定义别名方便使用
  2. type 可以定义基本数据类型,interface 不行;
  3. interface 可以合并重复声明,type不行
// 声明一个对象的类型时,有两个主要的工具:接口(interface)和类型别名(type aliases)。
//
// 他们非常相似,并且在大多数情况下是相同的。

type BirdType = {
  wings: 2;
};

interface BirdInterface {
  wings: 2;
}

const bird1: BirdType = { wings: 2 };
const bird2: BirdInterface = { wings: 2 };

// 因为 TypeScript 有着结构化类型系统。
// 我们也可以混合使用他们。

const bird3: BirdInterface = bird1;

// 他们都支持扩展另一个些接口或类型。
// 类型别名通过并集类型来实现,接口通过 extends 关键字。

type Owl = { nocturnal: true } & BirdType;
type Robin = { nocturnal: false } & BirdInterface;

interface Peacock extends BirdType {
  colourful: true;
  flies: false;
}
interface Chicken extends BirdInterface {
  colourful: false;
  flies: false;
}

let owl: Owl = { wings: 2, nocturnal: true };
let chicken: Chicken = { wings: 2, colourful: false, flies: false };

// 也就是说,我们建议您使用接口而不是类型别名,因为你可以在接口中获得更好的错误提示。
// 如果你将鼠标悬停在下面的错误上,你会看到在使用接口(例如 Chicken)时,
// TypeScript 会提供更简洁的提示信息。

owl = chicken;
chicken = owl;

// 一个接口和类型别名的主要区别是,接口是开放的,类型别名是封闭的。
// 这意味着你可以你可以通过多次声明同一个接口来扩展它。

interface Kitten {
  purrs: boolean;
}

interface Kitten {
  colour: string;
}

// 与此同时,类型别名不可以在外部变更它的声明。

type Puppy = {
  color: string;
};

type Puppy = {
  toys: number;
};

// 基于你不同的目的,这个区别可以是证明的也可以是负面的。
// 一般来说,对于公开的需要暴露的类型,将他们作为接口是更好的选择。

// 要查看接口和类型定义之间所有边际条件,下面的 StackOverflow 讨论是最好的资源之一:

// https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types/52682220#52682220

4. 泛型

1. 什么时候需要泛型

image.png

2. 泛型是什么

泛型(Generics)是一种参数化类型的机制,可以让我们在定义函数、类和接口时,使用一个或多个类型作为参数来指定其返回值成员的类型。这样可以提高代码的复用性和可读性,同时也能保证类型安全。

就是临时占个位置, 之后通过传递过来的参数进行推导

使用泛型时,我们需要在函数、类或接口名称后面加上尖括号 <T>,其中 T 代表泛型类型参数名。然后就可以在函数内部或类/接口成员的声明中使用这个泛型类型了。

3. 如何使用

下面是一个简单的泛型函数示例,它将传入的数组反转并返回:

function reverse<T>(array: T[]): T[] {
  return array.reverse();
}

let numbers = [1, 2, 3];
let reversedNumbers = reverse(numbers); // inferred type: number[]
console.log(reversedNumbers); // Output: [3, 2, 1]

let words = ["apple", "banana", "cherry"];
let reversedWords = reverse(words); // inferred type: string[]
console.log(reversedWords); // Output: ["cherry", "banana", "apple"]

在上面的代码中,我们定义了一个泛型函数 reverse,它接受一个类型为 T 的数组参数,并返回一个类型为 T 的数组。使用推断类型的方式调用这个函数时,TypeScript 会自动根据传入参数的类型推导出泛型类型 T 的具体类型。

需要注意的是,在使用泛型时,我们还可以对泛型类型参数进行约束,以限制它们只能是某个特定类型或其子类型。例如:

interface Lengthwise {
  length: number;
}

function logLength<T extends Lengthwise>(arg: T): void {
  console.log(arg.length);
}

let str = "hello";
logLength(str); // Output: 5

let obj = { length: 10 };
logLength(obj); // Output: 10

let num = 123;
logLength(num); // Error: 'number' has no 'length' property

在上面的代码中,我们定义了一个接口 Lengthwise,它包含一个名为 length 的数值属性。然后定义了一个泛型函数 logLength,它接受一个泛型类型参数 T,但是要求 T 必须满足 extends Lengthwise,也就是说必须具有 length 属性。

最后使用 strobjnum 三个变量分别调用 logLength 函数,可以看到只有 strobj 能够成功输出结果,而 num 因为没有 length 属性而导致编译错误。

4. 泛型工具类型-基础操作符

image.png

image.png

5. TS 实战

声明文件

  • declare : 第三方库需要类型声明文件
  • .d.ts : 声明文件定义
  • @type: 第三方库TS类型包
  • tsconfig.json: 定义TS的配置
配置分类(compilerOptions 选项)
{
  "compilerOptions": {
  
    /* 基本选项 */
    "target": "es5",                       // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
    "module": "commonjs",                  // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
    "lib": [],                             // 指定要包含在编译中的库文件
    "allowJs": true,                       // 允许编译 javascript 文件
    "checkJs": true,                       // 报告 javascript 文件中的错误
    "jsx": "preserve",                     // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
    "declaration": true,                   // 生成相应的 '.d.ts' 文件
    "sourceMap": true,                     // 生成相应的 '.map' 文件
    "outFile": "./",                       // 将输出文件合并为一个文件
    "outDir": "./",                        // 指定输出目录
    "rootDir": "./",                       // 用来控制输出目录结构 --outDir.
    "removeComments": true,                // 删除编译后的所有的注释
    "noEmit": true,                        // 不生成输出文件
    "importHelpers": true,                 // 从 tslib 导入辅助工具函数
    "isolatedModules": true,               // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).

    /* 严格的类型检查选项 */
    "strict": true,                        // 启用所有严格类型检查选项
    "noImplicitAny": true,                 // 在表达式和声明上有隐含的 any类型时报错
    "strictNullChecks": true,              // 启用严格的 null 检查
    "noImplicitThis": true,                // 当 this 表达式值为 any 类型的时候,生成一个错误
    "alwaysStrict": true,                  // 以严格模式检查每个模块,并在每个文件里加入 'use strict'

    /* 额外的检查 */
    "noUnusedLocals": true,                // 有未使用的变量时,抛出错误
    "noUnusedParameters": true,            // 有未使用的参数时,抛出错误
    "noImplicitReturns": true,             // 并不是所有函数里的代码都有返回值时,抛出错误
    "noFallthroughCasesInSwitch": true,    // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)

    /* 模块解析选项 */
    "moduleResolution": "node",            // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
    "baseUrl": "./",                       // 用于解析非相对模块名称的基目录
    "paths": {},                           // 模块名到基于 baseUrl 的路径映射的列表
    "rootDirs": [],                        // 根文件夹列表,其组合内容表示项目运行时的结构内容
    "typeRoots": [],                       // 包含类型声明的文件列表
    "types": [],                           // 需要包含的类型声明文件名列表
    "allowSyntheticDefaultImports": true,  // 允许从没有设置默认导出的模块中默认导入。

    /* Source Map Options */
    "sourceRoot": "./",                    // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
    "mapRoot": "./",                       // 指定调试器应该找到映射文件而不是生成文件的位置
    "inlineSourceMap": true,               // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
    "inlineSources": true,                 // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性

    /* 其他选项 */
    "experimentalDecorators": true,        // 启用装饰器
    "emitDecoratorMetadata": true          // 为装饰器提供元数据的支持
  }
}

6. 项目总结

image.png

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值