Typed JavaScript at Any Scale.
本文针对 TypeScript 的基础知识进行查漏补缺(可能有些个性你曾经在我的项目内纯熟应用了,然而并不分明它的实际意义和解决形式),如果心愿系统地进行学习,你能够从官网文档开始!
一些题外话:前段时间,在开始一个新我的项目的过程中,一个后端同学心愿参加到其中,并保持要用 JavaScript
(置信这种状况只是多数,其实他都不理解 JavaScript 和 TypeScript,仅仅是讨厌 TypeScript 所带来的学习老本),所以这里咱们就从介绍 TypeScript
的劣势开始!
- The main benefit of TypeScript is that it can highlight unexpected behavior in your code, lowering the chance of bugs.
- While the size, scope, and complexity of programs written in JavaScript has grown exponentially, the ability of the JavaScript language to express the relationships between different units of code has not.
- By understanding JavaScript, TypeScript saves you time catching errors and providing fixes before you run code.
简而言之就是,TypeScript 可能在加强代码的健壮性和可读性的同时,升高其可保护老本,尤其是保护一个大型项目时,其劣势更为显著!
TypeScript 并不是“洪水猛兽”,咱们能够Gradual Adoption(逐渐采纳),从一些简略的文件开始进行学习和开发!
???? 如果你是一个初学者,强烈推荐在 TypeScript Playground 上来进行测试和学习
DataTypes
???? We encourage the use of
--strictNullChecks
when possible!
在 JavaScript
内,曾经提供了一些原始类型:boolean
, bigint
, null
, number
, string
, symbol
, object
和 undefined
在此基础上,TypeScript
开发了一些扩大的类型来解决不同的场景:
- any 容许任意类型,次要用于兼容第三方库
-
unkonwn
like you can't predict the user input
- 用来告诉编译器和将来的读者,这个变量能够是任意类型
- 如果你应用了一个该类型的变量,你能够通过类型判断来进行解决
- never 用来示意一个不可能呈现的值类型,通常用在错误处理
- void 用于函数没有任何返回值或者返回值为
undefined
,return null
会报错噢
Nullable
在 TypeScript 内,null
和 undefined
是比拟非凡的存在,它们能够被赋值到任意类型,并且你不能阻止这类操作
TypeScript 实现了 strictNullChecks 用来保障在正确的机会进行类型查看,即当你申明一个变量时,它不再主动蕴含 null
或者 undefined
当你的值可能为 null
或者 undefined
时,编译器会报错,从而在外层阻止一些“危险”的操作
Tuple
Tuple
应该是开发过程中常常用到的一个类型,它容许你申明一个具备特定程序和数量的类型数组,这意味着你必须依照特定程序和类型来进行取值和赋值操作
let t:[string, number] = ["hello", 1]
// error: Type 'number' is not assignable to type 'string'.(2322)
t = [1, "world"]
Enums
TypeScript 在其 handbook 内花了一章的篇幅来介绍 enums
,以至于我想理解一个 枚举 类型为何有如此魔力
- 它绝对于 JavaScript 具备哪些个性?
- 它在开发时可能为咱们提供何种便当?
enums
容许开发者定义一系列的命名常量,这有助于浏览代码和创立一组不同的案例,先来理解根本个性,留神 enums
的成员变量命名首字母大写
对于枚举的个性,次要是取值,看一下它的编译文件就很分明了:
// define a easy enums case
enum Hello {
A,
B,
C
}
// after compile
var Hello;
(function (Hello) {// Hello["A"] = 0 返回值为 0,这一步的操作,最初输入 Hello {"A": 0, 0: "A"}
Hello[Hello["A"] = 0] = "A";
Hello[Hello["B"] = 1] = "B";
Hello[Hello["C"] = 2] = "C";
})(Hello || (Hello = {}));
emuns
联合 keyof
关键字能够生成一个字符类型的 union,其参数为 enums
内的成员变量的名字,参考 transfer-to-union 进行了解,这个个性挺棒的,期待在我的项目内进行实际
此外,TypeScript
提供了一些很酷的货色:
interface
和type
两种语法来创立自定义的 typeunion
和generics(范型)
来创立简单的数据结构as
通过断言来通知编译器trust me, I know what I’m doing.
Interface
In JavaScript, some design patterns make it difficult for types to be inferred automatically.
To cover this case, TypeScript supports an extension of the JavaScript language, which offers places for you to tell TypeScript what the types should be.
就像在 Go 内 interface
的语义一样,interface
提供了 相似多态 的类型验证
Typescript 内的一个外围法令就是:类型查看专一于值具备的类型,相似 duck typing
的概念,“如果它走路像鸭子,叫的像鸭子,那么它就是鸭子”
interface
作为 TypeScript 新设计的类型,它能够用来形容 JavaScript 内丰盛且灵便的类型,为此,它提供了一些很棒的个性:
-
readonly
- 一些属性只有在创立的时候才可能被批改,这时候能够通过
readonly
来实现,同时TypeScript
提供ReadonlyArray<T>
类型来解决不可变数组 - ????:申明 不可变属性 时,应用
readonly
,当申明一个 不可变变量 时,应用const
- 一些属性只有在创立的时候才可能被批改,这时候能够通过
-
过多属性查看
- 传递的属性内含有
interface
内未定义的属性则会触发 TypeScript 内的过多属性查看,即对传递属性的数量、类型(不包含程序)进行查看 - 首选的解决方案:TypeScript 提供了一个好的解决方案
[propname: string]:any
来容许任意键值对 - 同时,通过断言也能够来绕过这种类型查看,同时还有一种比拟 hack 办法,将值赋值给一个新的变量来进行传递也能够对该类型查看进行躲避
- 此外,
[index: number]: string
能够用来示意可索引类型,你应该相熟index
签名模式,它在开发过程中的确可能带来很多便当
- 传递的属性内含有
- 一个
interface
能够通过extends
关键字来延长 多个interface
,比方interface A extends B,C {}
Generics
范型和枚举一样,是为数不多的,原生 JavaScript 不具备的个性
A major part of software engineering is building components that not only have well-defined and consistent APIs, but are also reusable.
Components that are capable of working on the data of today as well as the data of tomorrow will give you the most flexible capabilities for building up large software systems.
范型的设计和实现就是为了解决组件复用的问题,我了解它就是一种形象,相似函数:承受输出的参数(类型),做肯定转换后,输入相应的类型
来看上面这个例子,咱们心愿定义一个函数,并且保障其输出类型和输入类型保持一致
- 如果参数类型只有一种,咱们能够间接用该类型来规约
- 如果输出 / 输入类型不止一种,你可能会想到用
union
或者any
来做这件事,然而这些伎俩并不可能保障其一致性
所以,看看范型是怎么做的:
// 定义一个函数,并且通过范型来保障输出类型和输入类型统一
function test<T>(args: T): T {return args}
// 调用这个函数,当然你也能够省略 `<string>`,将类型推断交给编译器去解决
// 根底类型举荐省略的写法,简单的类型举荐显示指定
let t = test<string>("hello")
// 定义一个函数类型
interface GenetateTest<U> {(args: U): U
}
// 同时,咱们也能够这样做
// 弄清楚范型签名放的地位,会给咱们提供极大的便当
interface GenetateOtherTest {<U>(args: U): U
}
let t2: GenerateTest = t
应用范型一个提供了极佳的形象,然而同时也导致了一些问题,实际上,能够了解 <T>
为任意值,然而有些属性只属于特定类型的值,这里就会产生抵触,为此 TypeScript 提供了一些约束条件来保障程序的运行,参考 Generics Constrait 进行了解
Union
集体认为,union
是 TypeScript 内一个十分棒的个性,它提供了极大的便利性和兼容性,在学习官网文档的时候,它的一个个性让我对它有了更多的设想,它就是 Discriminating Unions(分别组合)
- 这里插一句,TypeScript 同样提供
&
关键字(An intersection type combines multiple types into one)来对多个类型进行合并操作
interface TestA {
name: "a"
age: number
}
interface TestB {
name: "b"
surname: string
}
interface TestC {
name: "c"
height: number
}
type Person = TestA | TestB | TestC
function testPerson(data: Person) {
// Property 'age' does not exist on type 'Person'.
// Property 'age' does not exist on type 'TestB'.(2339)
console.log(data.age)
// 咱们能够利用 ** 文本类型 ** 来进行判断
switch (data.name) {
case 'a': {console.log(data.age)
break
}
case 'b': {console.log(data.surname)
break
}
case 'c': {console.log(data.height)
break
}
}
}
Functions
TypeScript also adds some new capabilities to the standard JavaScript functions to make them easier to work with.
TypeScript 提供了两种形式来反对 Function
类型查看
- 为每个参数增加类型,同时返回一个类型
const add = (x: number, y: number): number {return x + y}
- 编写一个函数类型,此时必须通过
=>
来指定return
类型
const add: (x: number, y: number) => number = (x: number, y: number) {return x + y}
形参
在 TypeScript 中,会默认查看每一个形参。这点很显著不同于 JavaScript,在 JavaScript 内,你能够传递任意属性的形参,对于函数内未定义的形参,会被赋值为 undefined
在 TypeScript 内,咱们能够应用 ?
关键字、默认值以及 Rest Parameters(能够看作是有限数量的可选参数)来进行更加灵便的配置,然而有一些点须要留神:
- 个别将
?
示意的可选参数放在参数列表的最初 - 默认值参数能够放在形参列表的任意地位,传参时通过
undefined
进行占位 - 在 JavaScript 内,能够通过
arguments
来获取传参,TypeScript 同样提供这样的能力,即通过(...restOfName: string[])
这种模式,能够收集那些残余参数到一个变量内
对于 this
Arrow functions capture the
this
where the function is created rather than where it is invoked
在 TypeScript 内,能够开启 --noImplictThis
来告诉编译器去查看 this
在定义时可能呈现的问题
如果没有显示地定义 this
类型,会默认其类型为 any
,因而你能够通过显示地定义 this
的类型来防止一些谬误
interface Demo {
name: string
setName(this: Demo): () => void}
overloads
一个乏味的个性,因为 JavaScript 实质上是一个相当动静的语言,因而一个函数通常能够承受不同类型的参数并且输入不同类型的后果
这个时候,咱们如何为函数增加类型验证呢?来看一个官网的例子:
// these two are overloads
function pickCard(x: { suit: string; card: number}[]): number;
function pickCard(x: number): {suit: string; card: number};
// this not the overload
function pickCard(x: any): any {
// Check to see if we're working with an object/array
// if so, they gave us the deck and we'll pick the card
if (typeof x == "object") {let pickedCard = Math.floor(Math.random() * x.length);
return pickedCard;
}
// Otherwise just let them pick the card
else if (typeof x == "number") {let pickedSuit = Math.floor(x / 13);
return {suit: suits[pickedSuit], card: x % 13 };
}
}
编译器会依据程序顺次进行匹配,因而,通常将简单的 overloads 放在后面
Classes
A class declaration creates two things: a type representing instances of the class and a constructor function!
如果你腻烦了在 JavaScript 内通过函数和原型的形式来创立组件,那么通过类来创立是一个不错的尝试!
咱们先从一个简略的例子开始
在这个例子中,咱们须要留神:
- 前置成员变量,示意其有成员拜访权限
- 在 TypeScript 内,在调用 constructor 内的其余属性之前,必须 强制执行
super()
办法
Class 在被申明时会产生两个动作:
- 创立一个代表类实例的类型,因而能够反对
interface extends classes
的写法 - 创立一个构造函数
成员属性
和其余强类型语言一项,TypeScript 内的类也具备成员属性的概念,它提供 public
、private
、proteced
以及 readonly
,默认为 public
这里比拟非凡的是 private
,TypeScript 既反对 ECMAScript 的语法(#
)也有本人的语法(private
)
- 相较之下,
#
的写法内置在 JavaScript 的 runtime,因而它可能更好地保障公有字段的隔离 private
的特点在于,即便两个类截然不同,然而只有蕴含private
字段,则它们在类型断定上是不相等的,参考 Y-lonelY/private 进行了解
对于 protected
总结了一些点,具备能够参考 understanding-protected 来进行了解
- 父类内定义的
protected
属性,能够在其子类中进行拜访,然而其实例(子类和父类的实例)不能拜访 - 如果对父类的构造函数增加
protected
标志符,则不能将其进行实例化
Todo
- Advanced Features
- Compile Configs
Thanks for reading, solo with code!????