}
function identity(arg: T): T {
console.log(arg.length); // 可以获取length属性
return arg;
}
T extends Length
用于告诉编译器,我们支持已经实现 Length
接口的任何类型。之后,当我们使用不含有 length
属性的对象作为参数调用 identity
函数时,TypeScript 会提示相关的错误信息:
identity(68); // Error
// Argument of type ‘68’ is not assignable to parameter of type ‘Length’.(2345)
此外,我们还可以使用 ,
号来分隔多种约束类型,比如:<T extends Length, Type2, Type3>
。而对于上述的 length
属性问题来说,如果我们显式地将变量设置为数组类型,也可以解决该问题,具体方式如下:
function identity(arg: T[]): T[] {
console.log(arg.length);
return arg;
}
// or
function identity(arg: Array): Array {
console.log(arg.length);
return arg;
}
4.2 检查对象上的键是否存在
泛型约束的另一个常见的使用场景就是检查对象上的键是否存在。不过在看具体示例之前,我们得来了解一下 keyof
操作符,keyof
操作符是在 TypeScript 2.1 版本引入的,该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。 “耳听为虚,眼见为实”,我们来举个 keyof
的使用示例:
interface Person {
name: string;
age: number;
location: string;
}
type K1 = keyof Person; // “name” | “age” | “location”
type K2 = keyof Person[]; // number | “length” | “push” | “concat” | …
type K3 = keyof { [x: string]: Person }; // string | number
通过 keyof
操作符,我们就可以获取指定类型的所有键,之后我们就可以结合前面介绍的 extends
约束,即限制输入的属性名包含在 keyof
返回的联合类型中。具体的使用方式如下:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
在以上的 getProperty
函数中,我们通过 K extends keyof T
确保参数 key 一定是对象中含有的键,这样就不会发生运行时错误。这是一个类型安全的解决方案,与简单调用 let value = obj[key];
不同。
下面我们来看一下如何使用 getProperty
函数:
enum Difficulty {
Easy,
Intermediate,
Hard
}
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let tsInfo = {
name: “Typescript”,
supersetOf: “Javascript”,
difficulty: Difficulty.Intermediate
}
let difficulty: Difficulty =
getProperty(tsInfo, ‘difficulty’); // OK
let supersetOf: string =
getProperty(tsInfo, ‘superset_of’); // Error
在以上示例中,对于 getProperty(tsInfo, 'superset_of')
这个表达式,TypeScript 编译器会提示以下错误信息:
Argument of type ‘“superset_of”’ is not assignable to parameter of type
‘“difficulty” | “name” | “supersetOf”’.(2345)
很明显通过使用泛型约束,在编译阶段我们就可以提前发现错误,大大提高了程序的健壮性和稳定性。接下来,我们来介绍一下泛型参数默认类型。
五、泛型参数默认类型
在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推断出类型时,这个默认类型就会起作用。
泛型参数默认类型与普通函数默认值类似,对应的语法很简单,即 <T=Default Type>
,对应的使用示例如下:
interface A<T=string> {
name: T;
}
const strA: A = { name: “Semlinker” };
const numB: A = { name: 101 };
泛型参数的默认类型遵循以下规则:
-
有默认类型的类型参数被认为是可选的。
-
必选的类型参数不能在可选的类型参数后。
-
如果类型参数有约束,类型参数的默认类型必须满足这个约束。
-
当指定类型实参时,你只需要指定必选类型参数的类型实参。未指定的类型参数会被解析为它们的默认类型。
-
如果指定了默认类型,且类型推断无法选择一个候选类型,那么将使用默认类型作为推断结果。
-
一个被现有类或接口合并的类或者接口的声明可以为现有类型参数引入默认类型。
-
一个被现有类或接口合并的类或者接口的声明可以引入新的类型参数,只要它指定了默认类型。
六、泛型条件类型
在 TypeScript 2.8 中引入了条件类型,使得我们可以根据某些条件得到不同的类型,这里所说的条件是类型兼容性约束。尽管以上代码中使用了 extends
关键字,也不一定要强制满足继承关系,而是检查是否满足结构兼容性。
条件类型会以一个条件表达式进行类型关系检测,从而在两种类型中选择其一:
T extends U ? X : Y
以上表达式的意思是:若 T
能够赋值给 U
,那么类型是 X
,否则为 Y
。在条件类型表达式中,我们通常还会结合 infer
关键字,实现类型抽取:
interface Dictionary<T = any> {
}
type StrDict = Dictionary
type DictMember = T extends Dictionary ? V : never
type StrDictMember = DictMember // string
在上面示例中,当类型 T 满足 T extends Dictionary
约束时,我们会使用 infer
关键字声明了一个类型变量 V,并返回该类型,否则返回 never
类型。
在 TypeScript 中,
never
类型表示的是那些永不存在的值的类型。例如,never
类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。
另外,需要注意的是,没有类型是
never
的子类型或可以赋值给never
类型(除了never
本身之外)。即使any
也不可以赋值给never
。
除了上述的应用外,利用条件类型和 infer
关键字,我们还可以方便地实现获取 Promise 对象的返回值类型,比如:
async function stringPromise() {
return “Hello, Semlinker!”;
}
interface Person {
name: string;
age: number;
}
async function personPromise() {
return { name: “Semlinker”, age: 30 } as Person;
}
type PromiseType = (args: any[]) => Promise;
type UnPromisify = T extends PromiseType ? U : never;
type extractStringPromise = UnPromisify; // string
type extractPersonPromise = UnPromisify; // Person
七、泛型工具类型
为了方便开发者 TypeScript 内置了一些常用的工具类型,比如 Partial、Required、Readonly、Record 和 ReturnType 等。出于篇幅考虑,这里我们只简单介绍其中几个常用的工具类型。
7.1 Partial
Partial<T>
的作用就是将某个类型里的属性全部变为可选项 ?
。
定义:
/**
* node_modules/typescript/lib/lib.es5.d.ts
* Make all properties in T optional
*/
type Partial = {
[P in keyof T]?: T[P];
};
在以上代码中,首先通过 keyof T
拿到 T
的所有属性名,然后使用 in
进行遍历,将值赋给 P
,最后通过 T[P]
取得相应的属性值。中间的 ?
号,用于将所有属性变为可选。
示例:
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial) {
return { …todo, …fieldsToUpdate };
}
const todo1 = {
title: “organize desk”,
description: “clear clutter”
};
const todo2 = updateTodo(todo1, {
description: “throw out trash”
});
在上面的 updateTodo
方法中,我们利用 Partial<T>
工具类型,定义 fieldsToUpdate
的类型为 Partial<Todo>
,即:
{
title?: string | undefined;
description?: string | undefined;
}
7.2 Record
Record<K extends keyof any, T>
的作用是将 K
中所有的属性的值转化为 T
类型。
定义:
/**
* node_modules/typescript/lib/lib.es5.d.ts
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
};
示例:
interface PageInfo {
title: string;
}
type Page = “home” | “about” | “contact”;
const x: Record<Page, PageInfo> = {
about: { title: “about” },
contact: { title: “contact” },
home: { title: “home” }
};
7.3 Pick
Pick<T, K extends keyof T>
的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。
定义:
// node_modules/typescript/lib/lib.es5.d.ts
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
};
示例:
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, “title” | “completed”>;
const todo: TodoPreview = {
title: “Clean room”,
completed: false
};
7.4 Exclude
Exclude<T, U>
的作用是将某个类型中属于另一个的类型移除掉。
定义:
// node_modules/typescript/lib/lib.es5.d.ts
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
如果 T
能赋值给 U
类型的话,那么就会返回 never
类型,否则返回 T
类型。最终实现的效果就是将 T
中某些属于 U
的类型移除掉。
示例:
type T0 = Exclude<“a” | “b” | “c”, “a”>; // “b” | “c”
type T1 = Exclude<“a” | “b” | “c”, “a” | “b”>; // “c”
type T2 = Exclude<string | number | (() => void), Function>; // string | number
7.5 ReturnType
ReturnType<T>
的作用是用于获取函数 T
的返回类型。
定义:
// node_modules/typescript/lib/lib.es5.d.ts
/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (…args: any) => any> = T extends (…args: any) => infer R ? R : any;
示例:
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<() => T>; // {}
type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // number[]
type T4 = ReturnType; // any
type T5 = ReturnType; // any
type T6 = ReturnType; // Error
type T7 = ReturnType; // Error
简单介绍了泛型工具类型,最后我们来介绍如何使用泛型来创建对象。
八、使用泛型创建对象
8.1 构造签名
有时,泛型类可能需要基于传入的泛型 T 来创建其类型相关的对象。比如:
class FirstClass {
id: number | undefined;
}
class SecondClass {
name: string | undefined;
}
class GenericCreator {
create(): T {
return new T();
}
}
const creator1 = new GenericCreator();
const firstClass: FirstClass = creator1.create();
const creator2 = new GenericCreator();
const secondClass: SecondClass = creator2.create();
在以上代码中,我们定义了两个普通类和一个泛型类 GenericCreator<T>
。在通用的 GenericCreator
泛型类中,我们定义了一个名为 create
的成员方法,该方法会使用 new 关键字来调用传入的实际类型的构造函数,来创建对应的对象。但可惜的是,以上代码并不能正常运行,对于以上代码,在 TypeScript v3.9.2 编译器下会提示以下错误:
‘T’ only refers to a type, but is being used as a value here.
这个错误的意思是:T
类型仅指类型,但此处被用作值。那么如何解决这个问题呢?根据 TypeScript 文档,为了使通用类能够创建 T 类型的对象,我们需要通过其构造函数来引用 T 类型。对于上述问题,在介绍具体的解决方案前,我们先来介绍一下构造签名。
在 TypeScript 接口中,你可以使用 new
关键字来描述一个构造函数:
interface Point {
new (x: number, y: number): Point;
}
以上接口中的 new (x: number, y: number)
我们称之为构造签名,其语法如下:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
总结
前端资料汇总
-
框架原理真的深入某一部分具体的代码和实现方式时,要多注意到细节,不要只能写出一个框架。
-
算法方面很薄弱的,最好多刷一刷,不然影响你的工资和成功率😯
-
在投递简历之前,最好通过各种渠道找到公司内部的人,先提前了解业务,也可以帮助后期优秀 offer 的决策。
-
要勇于说不,对于某些 offer 待遇不满意、业务不喜欢,应该相信自己,不要因为当下没有更好的 offer 而投降,一份工作短则一年长则 N 年,为了幸福生活要慎重选择!!!
喜欢这篇文章文章的小伙伴们点赞+转发支持,你们的支持是我最大的动力!
.(img-wkZwjNJb-1711813130700)]
[外链图片转存中…(img-qQs1WaQt-1711813130701)]
[外链图片转存中…(img-2UVCO9md-1711813130701)]
[外链图片转存中…(img-qpixx81A-1711813130702)]
[外链图片转存中…(img-W0x8RLyp-1711813130702)]
[外链图片转存中…(img-xgQChICE-1711813130703)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-yQnRQu4d-1711813130703)]
总结
前端资料汇总
-
框架原理真的深入某一部分具体的代码和实现方式时,要多注意到细节,不要只能写出一个框架。
-
算法方面很薄弱的,最好多刷一刷,不然影响你的工资和成功率😯
-
在投递简历之前,最好通过各种渠道找到公司内部的人,先提前了解业务,也可以帮助后期优秀 offer 的决策。
-
要勇于说不,对于某些 offer 待遇不满意、业务不喜欢,应该相信自己,不要因为当下没有更好的 offer 而投降,一份工作短则一年长则 N 年,为了幸福生活要慎重选择!!!
喜欢这篇文章文章的小伙伴们点赞+转发支持,你们的支持是我最大的动力!