关于typescript:TypeScript-学习笔记

7次阅读

共计 9682 个字符,预计需要花费 25 分钟才能阅读完成。

Hello, 各位怯懦的小伙伴, 大家好, 我是你们的嘴强王者小五, 身体健康, 脑子没病.

自己有丰盛的脱发技巧, 能让你一跃成为资深大咖.

一看就会一写就废是自己的宗旨, 菜到抠脚是自己的特点, 低微中透着一丝丝坚强, 傻人有傻福是对我最大的刺激.

欢送来到 小五 随笔系列 TypeScript 学习笔记.

导读

本文初衷为笔者在应用一段时间 TS 后, 对所学所想做一个记录. 文章内容较为根底, 适宜作为入门级教程学习; 且涵盖内容并不全面, 缺失蕴含类在内的诸多内容. 如果各位看官不介意以上几点, 欢送与笔者一起进入这谨严却又妙不可言的奇幻旅途.

根底类型

Boolean – 布尔

let flag: boolean = false;

Number – 数字

let num: number = 10;

👺 扩大: number 类型还反对 二进制 (0b 结尾) 八进制 (0o 结尾) 十六进制(0x 结尾) 字面量. 运算或输入时会转换为对应的十进制.

let num02: number = 0b1010;
let num08: number = 0o744;
let num16: number = 0xf00d;

String – 字符串

let str: string = '黄刀小五';

Array – 数组

数组共有两种定义形式: Array<T>T[] (T 为该数组每项的元素类型)

let numArr: number[] = [1, 2, 3];
let numArr: Array<number> = [1, 2, 3];

Tuple – 元组

👺 特点: < 数组 >, < 长度已知 >, < 每项元素类型不尽相同 >

😼 就是一个给每项都定义数据类型的数组

let tuple: [string, string, number, boolean?] = ['No.1', '黄刀小五', 18];

🦥 利用:

  • React Hook 的 useState
import {useState} from 'react';
const [loading, setLoading] = useState<boolean>(false); // 这里类型可省略, 详见后文类型推断
  • csv 数据格式
type Touple = [string, number, string];
let csvData: Touple[] = [['黄刀小五', 18, '男'], ['二狗子', 14, '男']];

👺 扩大: 元组越界

let tuple: [number, string] = [1, '黄刀小五'];
tuple.push(2); // ✅ right 元组越界时, 该项类型相当于对元组的各项类型做联结

/* 过程如下, 看不懂的看官莫慌, 请先看后文 */
type UnionType<T> = T extends (infer P)[] ? P : never;
type TupleType = UnionType<typeof tuple>; // number | string

Enum – 枚举

enum Active {
  inactive,
  active,
}

enum Fruit {
  apple = 'apple',
  orange = 'orange',
  banana = 'banana',
}

若枚举类型未指定值或指定的值为 number 类型, 如上述 Active, 可对其进行双向取值: Active[0]Active['active'], 其映射如下【👇】

{
  0: 'inactive',
  1: 'active',
  inactive: 0,
  active: 1,
}

可对枚举的其中一项进行指定数值(通常为第一项), 其余项会程序递增

enum Type {
  active = 1,
  inactive,
}

{ // 对应映射为
  1: 'active',
  2: 'inactive',
  active: 1,
  inactive: 2,
}

😼 tips: 倡议采纳赋值模式的枚举. 可读性更高, 如上述 Fruit

🦥 利用:

  • 联合 switch … case 应用:
enum ActionType {
  ADD = 'ADD',
  EDIT = 'EDIT',
  DELETE = 'DELETE',
}

const reducer = (type: ActionType) => {switch(type) {
    case ActionType.ADD:
      // xxx
      break
    case ActionType.EDIT:
      // xxx
      break
    ...
  }
}

reducer(ActionType.ADD); // ✅ right
let params = 'ADD';
reducer(params); // ❎ error (类型“string”的参数不能赋给类型“ActionType”的参数)

/* 😼 tips: 非赋值模式大家可自行尝试 */
  • 定义类型映射 或 定义一组常量
enum Status { // 类型映射
  unprocessed = 1, // 未解决
  processed, // 已解决
  refused, // 已回绝
}

enum Fruit { // 常量
  apple = '苹果',
  orange = '橘子',
  banana = '香蕉',
}

👺 扩大: keyof typeof Enum, 可将枚举类型转换为联结类型; 置信我, 你会用到的😏

enum ActionType {
  ADD,
  EDIT,
  DELETE,
}
type ActionTypeConst = keyof typeof ActionType // 'ADD' | 'EDIT' | 'DELETE'

Any – 任意类型

let value: any;

😼 tips: 食物链最顶端, 应尽量减少 any 的应用.

Unknown – 未知类型

let value: unknown;

unknown 示意未知类型, 与 any 不同的是无奈对 unknown 类型执行任何操作. 认真思考下 <any: 任意类型 >、<unknown: 未知类型 > 的区别.

👉 以 numbertoFixed() 办法举例:

  • unknown 代表我不晓得这是什么类型, 而只有 numbertoFixed() 办法, 故不能应用;
  • any 代表我能够是任意类型, 此时调用 toFixed() 办法的我就是 number 类型;

😼 tips: 如想应用 any, 请先思考是否可用 unknown 代替. (搭配后文类型爱护可安心食用)

Void – 无类型

罕用于没有具体返回值的函数

const fn = (str: string): void => {// 执行 xxx 事件}

Null 和 Undefined

let u: undefined = undefined;
let n: null = null;

这两个不晓得要说点啥, 就补充以下两点吧 (然而与 TS 没啥关系)

  1. Number(undefined) => NaN, Number(null) => 0
  2. const fn = (arg?: string) => {...} arg 的类型是 string | undefined

Never – 永不返回

let n: never;
  • never 示意那些永远不存在的值, 当咱们不想捕捉以后值时, 可应用 never;
  • 任何类型都不可赋值给 never, 包含 any;
  • never | T = T 此个性可用来过滤掉不须要的值;

🦥 利用:

  • 做类型查看
type Type = string | number;

const fn = (arg: Type) => {if (typeof arg === 'string') {...}
  else if (typeof arg === 'number') {...}
  else {const check: never = arg;}
}

如上, 此时永远不会走到 else, 上面咱们做以下操作:

- type Type = string | number;
+ type Type = string | number | boolean;

* 此时 else 中的 check 会报错 (不能将类型“boolean”调配给类型“never”)

类型推论

如果没有指定类型, TS 会依据类型推论推断出一个类型.

let val; // 推论成: let val: any;
let num = 10; // 推论成: let num: number = 10;
num = '黄刀小五'; // ❎ error (不能将类型“string”调配给类型“number”)

🦥 利用: < 繁多动态类型 - 如上述 num>< 函数返回值 - 个别状况均可正确推断其返回值类型, 不必额定指定 >< 循环中的子元素 >

😼 tips: 如果 TS 能正确推断出其类型, 咱们可采纳类型推论而不用定义类型.

类型断言

类型断言用来通知编译器“我晓得本人在干什么”, 有 尖括号as 两种写法. 在 $tsx$ 语法中, 只反对 as.

* 😼 tips: 上面咱们应用数组来举个例子, 理论场景中应应用元组.

type Key = string | number;
let arr: Key[] = ['黄刀小五', 18];

// 不能将类型“Key”调配给类型“string”- let name = arr[0];
- console.log(name.length);

// 应用类型断言
+ let name = arr[0] as string;
+ let name = <string>arr[0];
+ console.log(name.length);

Interface – 接口

接口用来定义对象的类型

interface User {
  readonly id: number; // 只读属性, 不可批改
  name: string; // 必须属性
  desc?: string; // 可选属性
  say?: (name: string) => void; // 办法
}

const say = (name: string) => {...}
let user: User = {
  id: 1,
  name: '黄刀小五',
  say,
}

user.id = 2; // ❎ error (无奈调配到 "id",因为它是只读属性)

索引签名 – 使接口更加灵便

interface User {[key: string]: string; // 示意 key 为 string, value 为 string 的任意属性
}

let user:User = {
  name: '黄刀小五',
  desc: '菜鸡前端一只',
}

😼 tips: 所有成员都必须合乎索引签名的特色, 索引签名参数类型必须为 string | number

interface User {[key: string]: string;
  age: number; // ❎ error (类型“number”的属性“age”不能赋给字符串索引类型“string”。)
}

接口合并

interface User {name: string;}
interface User {age: number;}
/* 两者会合并为👇 */
interface User {
  name: string;
  age: number;
}

接口继承

关键字: extends

interface Person {name: string;}
interface User {age: number;}
interface Student extends Person, User {desc: string;}
/* Student 接口格局如下👇 */
interface Student {
  name: string;
  age: number;
  desc: string;
}

😼 tips: 接口和类型别名均可应用的状况下应用接口

🤔 思考: 如何定义一个树形构造

interface Tree {
  key: number;
  value: string;
  child?: Tree[];}
let tree: Tree[] = [];

类型别名

顾名思义, 为该类型取一个新的名字

type Key = string | number;

与 Interface 比照

  • type 不反对继承和申明合并, interface 能够, 参考上文;
  • type 更为通用, 右侧能够是任意类型, interface 次要用于定义对象;
  • typeinterface 均可应用的状况下应用 interface;

联结类型与穿插类型

  • 联结类型 (A | B)
type Key = string | number; // 代表 string 或 number 类型

😼 tips: 联结类型能够用来申明具体的值

type Status = 'active' | 'inactive';
  • 穿插类型 (A & B)
interface User {name: string;}
interface Student {age: number;}
type Blogger = User & Student;

/* Blogger 类型格局如下👇 */
{
  name: string;
  age: number;
}

😼 tips: 两个根底类型做穿插, 会生成 never 类型

type Key = string & number; // never, 没有类型能够满足即是 string 又是 number

类型查找

类型查找能够提取对象类型上某一属性的类型

interface Person {
  User: {
    name: string;
    age?: number;
  }
}
type User = Person['User']

😼 tips: 罕用于第三方库类型无奈援用的场合

罕用关键字

const

👺 配合类型断言 as 来申明常量

type Status = 'active' | 'inactive';
const fn = (status: Status) => {...}

- let status = 'active'; // 此时 'active' 被解析为字符串而十分量
- fn(status); // ❎ error (类型“string”的参数不能赋给类型“Status”的参数)

* 以下 3 种等价, 均可将 'active' 解析为常量
+ let status = 'active' as const;
+ const status = 'active';
+ let status: Status = 'active';
+ fn(status); // ✅ right

😼 tips: 第三方插件定义的常量常常须要配合 as const 应用呦

typeof

👺 用来获取变量的类型

let str = '黄刀小五';
type Str = typeof str; // type Str = string

let user = {name: '黄刀小五'}
type User = typeof user; // type User = {name: string;}

keyof

👺 用来提取对象类型的 key

interface User {
  name: string;
  age?: number;
}
type Key = keyof User; // type Key = 'name' | 'age'

类型爱护

为什么须要类型爱护

const fn = (value: string | number) => {if (value.length) {...} // ❎ error (类型“string | number”上不存在属性“length”)
}

上述代码, 如果想在 value: string 时执行一段逻辑要怎么办呢?

此时就须要类型爱护了, 应用类型爱护后, 以后代码段会依照当时所指定的类型执行.

👺 typeof

const fn = (value: string | number) => {if (typeof value === 'string') { // 利用 typeof 限度 value 的类型为 string
    console.log(value.length);
  }
}

👺 is

const isString = (x: unknown): x is string => {return typeof x === 'string';}
const fn = (value: string | number) => {if (isString(value)) console.log(value.length); // value is string
}

👺 in

示意某属性是否在以后对象中存在

interface User {
  name: string;
  age?: number;
}
const fn = (args: User) => {if ('age' in args) {...}
  else {...}
}

fn({name: '黄刀小五', age: 18}); // 执行 if 语句
fn({name: '黄刀小五'}); // 执行 else 语句

泛型

👺 作用: 用于做代码复用

先来看个例子

const fn = (value: number): number => value;

🤔 思考 如果此时我想传入一个 string 类型并返回一个 string 类型呢

const fn = <T>(value: T): T => value;
fn<string>('黄刀小五'); // const fn: <string>(value: string) => string

* 😼 tips: 可依据 类型推断 推断出其为 string 类型, 而不必特意指定 -> `fn('黄刀小五');`

以上, 一个泛型就实现好了, 其能够将类型作为一个参数, 在调用时传入类型进行指定.

👺 留神: 在 $tsx$ 中, <T> 会被解析成标签, 可应用上面的写法:

const fn = <T extends {}>(value: T): T => value; // extends 也可用于放大 T 的范畴
const fn = <T,>(value: T): T => value;
  • 传入多个类型:
const fn = <T, U>(type: T, value: U): U => {...};
fn<boolean, string>(true, '黄刀小五');

泛型的作用相当之广, 如定义一个接口:

interface Teacher<T> {
  readonly id: number;
  name: string;
  student?: T[];}
interface Student {
  readonly id: number;
  name: string;
  age?: number;
}
let teahcer: Teacher<Student> = {
  id: 1,
  name: '黄刀小五',
  student: [{
    id: 1001,
    name: '二狗子',
    age: 14,
  }]
}

罕用语法糖

上面咱们来用上述常识实现下 TS 中封装好的语法糖 (舒适提醒: 倡议先搞懂上文, 至多搞懂泛型在浏览上面内容) look down 👇

Readonly

👺 将对象类型的属性均变为只读

  • 代码实现
type Readonly<T> = {readonly [K in keyof T]: T[K];
};
  • Demo
interface Person {
  readonly name: string;
  age: number;
  desc?: string;
}
let person: Readonly<Person> = {
  name: '黄刀小五',
  age: 18,
}
person.age = 19; // ❎ error (无奈调配到 "age",因为它是只读属性)

Partial

👺 将对象类型的属性均变为可选

  • 代码实现
type Partial<T> = {[K in keyof T]?: T[K];
};
  • Demo
interface Person {
  readonly name: string;
  age: number;
  desc?: string;
}
let person: Partial<Person> = {}; // 此时所有属性均为可选

Required

👺 将对象类型的属性均变为必须

  • 代码实现
type Required<T> = {[K in keyof T]-?: T[K];
};

😼 tips: -? 示意去掉可选符号 ?, 此符号 () 同样可用于其它地位, 如: -readonly 可去掉只读属性等.

  • Demo
interface Person {
  readonly name: string;
  age: number;
  desc?: string;
}
let person: Required<Person>> = {
  name: '黄刀小五',
  age: 18,
  desc: '基于搜索引擎的复制粘贴工程狮',
}; // 此时所有属性均为必须

Record

👺 将一个类型的所有属性值映射到另一个类型上并创立一个新的类型

  • 代码实现
type Record<K extends string, T> = {[P in K]: T;
};
  • Demo
interface Person {
  readonly name: string;
  age: number;
  desc?: string;
}
type Kind = 'teacher' | 'student';
let person: Record<Kind, Person> = {
  teacher: {
    name: '黄刀小五',
    age: 18,
  },
  student: {
    name: '二狗子',
    age: 14,
  }
}; // 将 Person 类型映射到 Kind 类型中

/* Record<Kind, Person> 相当于👇 */
type NewPerson = {[key in Kind]: Person
};

Extract

👺 从 T 中提取 U

  • 代码实现
type Extract<T, U> = T extends U ? T : never;
  • Demo
type Student = '二狗子' | '如花';
type Teacher = '黄刀小五' | '二狗子';
type Trainee = Extract<Student, Teacher>; // 输入 '二狗子'

/* Extract<Student, Teacher> 相当于👇 */
'二狗子' in Teacher -> '二狗子'
'如花' in Teacher -> never
'二狗子' | never = '二狗子'

Exclude

👺 从 T 中排除 U

  • 代码实现
type Exclude<T, U> = T extends U ? never : T;
  • Demo
type Student = '二狗子' | '如花';
type Teacher = '黄刀小五' | '二狗子';
type OnlyStudent = Exclude<Student, Teacher>; // 输入 '如花'

/* Exclude<Student, Teacher> 相当于👇 */
'二狗子' in Teacher -> never
'如花' in Teacher -> '如花'
'如花' | never = '如花'

Pick

👺 筛选对象中的局部属性

  • 代码实现
type Pick<T, K extends keyof T> = {[P in K]: T[P];
}
  • Demo
interface Person {
  readonly name: string;
  age: number;
  desc?: string;
}
let person: Pick<Person, 'age' | 'desc'> = {age: 18,};

/* Pick<Person, 'age' | 'desc'> 相当于👇 */
interface Person {
  age: number;
  desc?: string;
}

Omit

👺 疏忽对象中的局部属性

  • 代码实现
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
  • Demo
interface Person {
  readonly name: string;
  age: number;
  desc?: string;
}
let person: Omit<Person, 'name'> = {age: 18,};

/* Omit<Person, 'name'> 相当于👇 */
interface Person {
  age: number;
  desc?: string;
}

ReturnType

👺 获取办法的返回值类型

  • 代码实现
type ReturnType<T> = T extends (...args: any[]
) => infer R ? R : any;

-> infer 配合 extends 应用, 用于推断函数的返回值类型

  • Demo
const getName = (name: string) => name;
type ReturnGetName = ReturnType<typeof getName>; // string

Parameters

👺 获取办法的参数类型

  • 代码实现
type Parameters<T> = T extends (...args: infer P) => any ? P : never;

-> 这里 infer 用于推断函数的参数类型, Parameters 返回格局为元组.

  • Demo
const getName = (name: string) => name;
type ParamGetName = Parameters<typeof getName>; // [name: string]

NonNullable

👺 排除 nullundefined 类型

  • 代码实现
type NonNullable<T> = T extends null | undefined ? never : T;
  • Demo
type NewPerson = Person | null;
let person: NonNullable<NewPerson> = null; // ❎ error (不能将类型“null”调配给类型“Person”)

结束语

联合文章内容, 大家可依据理论需要封装更多的语法糖, 便于在我的项目中应用. 若想更粗疏的学习 TS, 这里举荐一个博主 阿宝哥, 其发表了很多对于 TS 的文章, 并针对 TS 的某一特色做了具体的解说.

参考🔗链接

【TypeScript 文档】

【FESKY】联合实例学习 Typescript

【刘哇勇】TypeScript infer 关键字

正文完
 0