title: 与 TypeScript 相关的一些记录 layout: post thread: 252 date: 2020-07-12 author: Joe Jiang categories: Document tags: [2020, 前端, TypeScript, 类型系统, typing]
A type system specifies the type rules of a programming language independently of particular typechecking algorithms. This is analogous to describing the syntax of a programming language by a formal grammar, independently of particular parsing algorithms. —— Type Systems
本文记录两件事,一个是关于 typing 的一些名词释义,另一个便是结合 TypeScript 给出一些示例用于解释这门语言的能力。
说到 typing 分类,常见的维度分为以下几种:
而以上说到的最后一个分类也被用于引出 TypeScript 所采用的类型系统——结构类型系统。
其他还有一些名词:
关于类型系统有一本“小书”,也就几十页,但个人看起来还相当吃力,感兴趣可以移步
Cardelli, Luca. "Type systems." ACM Computing Surveys 28.1 (1996): 263-264.
TypeScript 和 C# 有着颇深的渊源,他们都是在微软大神 Anders Hejlsberg 的领导之下产生的编程语言,两者在诸多设计细节方面十分相似。然而,一个非常重要的不同之处在于,C# 采用的是 Nominal Type System(标明类型系统),TypeScript 考虑到 JavaScript 本身的灵活特性,采用的是 Structural Type System。
关于标明类型系统和结构类型系统的区别,可以看这个例子。这里是一段 C# 代码:
// 示例 from https://zhuanlan.zhihu.com/p/64446259
public class Foo
{
public string Name { get; set; }
public int Id { get; set;}
}
public class Bar
{
public string Name { get; set; }
public int Id { get; set; }
}
Foo foo = new Foo(); // Okay.
Bar bar = new Foo(); // Error!!!
Foo
和Bar
两个类的内部定义完全一致,但是当将Foo
实例赋值给Bar
类型的变量时编译器报错,说明两者的类型并不一致。标明类型系统比较的是类型本身,具备非常强的一致性要求。
TypeScript 则不太一样:
// 示例 from https://zhuanlan.zhihu.com/p/64446259
class Foo {
method(input: string): number { ... }
}
class Bar {
method(input: string): number { ... }
}
const foo: Foo = new Foo(); // Okay.
const bar: Bar = new Foo(); // Okay.
下面这个例子比较能够说明这一类型系统的灵活性:
// 示例 from https://zhuanlan.zhihu.com/p/64446259
type Point = {
x: number;
y: number;
};
function plot(point: Point) {
// ...
}
plot({ x: 10, y: 25 }); // Okay.
plot({ x: 8, y: 13, name: 'foo' }); // Extra fields Okay. Need enable `suppressExcessPropertyError`
一个典型便是泛型。软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
正常情况,我们会这样定义一个函数:
function identity(arg: number): number {
return arg;
}
若是 arg 改变类型,那么我们的函数可能又要改写成这样:
function identity(arg: string): string {
return arg;
}
因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 类型变量,是一种特殊的变量,只用于表示类型而不是值,如上示例可以写成:
function identity<T>(arg: T): T {
return arg;
}
这里举例一个有意思的类型 NonNullable,这里粘同事写的一个例子来说明:
function isNotNil<T>(x: T): x is NonNullable<T> {
return x != null;
}
const type_guard_demo_1 = [1, 2, '', undefined, null].filter(isNotNil);
假设我们在这里不明确写上 isNotNil 的返回类型,那么 TypeScript 给你的推断结果或许就是,至少 TypeScript playground 上还是这样:
const type_guard_demo_1: (string | number | null | undefined)[]
而对返回结果加上 NonNullable 声明,便可以得到你想要的结果:
const type_guard_demo_1: (string | number)[]
NonNullable<T>
做的事情便是从类型T中剔除 null 和 undefined,然后构造一个类型。而得益于 TypeScript 本身就是用 TypeScript 开发的,你可以很容易看到 NonNullable 的实现:
/**
* Exclude null and undefined from T
*/
type NonNullable<T> = T extends null | undefined ? never : T;
在你觉得类型不够用的时候,你还可以通过条件类型(Conditional Type)来创造更多的自定义工具类型。
以上,是对这两部分内容的一些笔记,但这不适用于针对 TypeScript 的整体入门,因为相关内容还是欠缺太少,相关内容应该会另起篇幅书写。另外,如果你想尝试写些 TypeScript 代码,TypeScript Playground 会是一个比较不错的选择。
参考的资料与可以进一步阅读的资料包含在如下链接中: