TypeScript 作为 JavaScript 的超集,让 JavaScript 越来越像一门“正经的”语言。和纯粹的 JavaScript 语言相比,TypeScript 提供静态的类型检查,提供类的语法糖(这一点是继承了 ES6 的实现),让程序更加规范和紧凑,为使用 JavaScript 开发大型项目提供了必要的条件。本文介绍 3 个加强理解的面试问题。
A 的类型是什么,为什么?
通常在面试中会有一些阅读程序语句给出结果的问题,一般是一段非常简单的代码,是检验候选人对基础的理解。
type A = number & string;
答案很简单,A 的类型是 never
,为什么呢?因为 &
是交集运算符,如果将字符串和数字视为一组值,则可以对其进行赋值。那么字符串集合和数字集合的交集是空集,在 TypeScript 中用 never
表示,换句话说,不存在可以同时属于数字和字符串的值:
运行时验证
假设候选人成功回答了第一个问题,这意味着大概率候选人对基础有了很好的理解。现在,需要变得更复杂一点,变得非常实际:假设有一个 API,它返回给我们一个 JSON 字符串,其中包含有关用户的信息。
问题是:如何验证用户对象,并确保它确实满足
User
类型?
type User = { name: string; age: number; }; const data = `{"name":"Bob","age":30}`; const user: User = JSON.parse(data); console.log(`Username: ${user.name.toLowerCase()}`); console.log(`Age: ${user.age.toFixed(0)}`);
答案基本上有两种方式:
首先,如果 User
类型很小,比如现在只有两个字段,这对我们来说没问题,那么我们可以编写一个简单的验证函数:
function isUser(obj: unknown): obj is User { return ( typeof obj['name'] === 'string' && typeof obj['age'] === 'number' ); }
并像这样使用它:
const data = `{"name":"Bob","age":30}`; const user = JSON.parse(data); // user type if any here if (isUser(user)) { console.log(`Username: ${user.name.toLowerCase()}`); console.log(`Age: ${user.age.toFixed(0)}`); }
第二种变体,它更方便——使用你喜欢的任何库来使用模式验证:class-validator
、zod
、runtypes
、joi
等:
import Joi from "joi"; const UserSchema = Joi.object({ name: Joi.string(), age: Joi.number(), }); const data = `{"name":"Bob","age":30}`; const userData = JSON.parse(data); try { const user = UserSchema.validate(userData); console.log(`Username: ${user.name.toLowerCase()}`); console.log(`Age: ${user.age.toFixed(0)}`); } catch (e) {}
这个问题不仅要检查有关数据验证的知识,还要检查候选人对技术堆栈的了解程度,可以通过哪些方式完成等。
在类型中使用递归
最后一个问题通常是实用的,它是关于如何编写递归类型的。在此示例中,需要问题的解决方案:假设正在编写一个函数 find()
来搜索数据库中的用户。问题是该类型 User
具有地址等属性,它也是一个对象。想以 find()
这种方式构建函数,这样可以通过 User
的嵌套属性进行搜索,并提供完整的类型支持:
function find<T>(criteria: ...): T[] { ... } type User = { id: number; name: string; address: { country: string; city: string; house: string; zipcode: string; }; }; // in this example im searching by country only, even if // address has other properties. const users = find({ address: { coutry: 'CN' } });
如何实现:创建另一个名为 DeepPartial
的类型,并在 find()
函数中使用它作为条件参数:
type DeepPartial<T> = { [P in keyof T]?: DeepPartial<T[P]>; }; function find<T>(criteria: DeepPartial<T>): T[] { ... } type User = { id: number; name: string; address: { country: string; city: string; house: string; zipcode: string; }; }; const users = find({ address: { coutry: 'UK' } });
DeepPartial<T>
几乎与 Partial<T>
一样工作,但唯一的区别是它递归地应用于对象的每个嵌套属性:
type User = { id: number; name: string; address: { country: string; city: string; house: string; zipcode: string; }; }; // DeepPartial<User> type DeepPartiallUser = { id?: number; name?: string; address?: { country?: string; city?: string; house?: string; zipcode?: string; }; };
希望通过上面这些 TypeScript 的小例子可以帮助更好地使用它,并且在还不知道这些东西的情况下写出更好的代码。