根据对 90,000 名开发人员的 Stack Overflow 调查,TypeScript 是人们最想学习的框架之一。前端
在过去几年中,TypeScript 的受欢迎程度,社区规模和采用率都在不断提升。 目前,Facebook 的 Facebook Jest 项目正在向 TypeScript 转移。typescript
TypeScript 是 JavaScript 的静态类型超集,提供了类型系统和对 ES6 的支持,它能够编译成纯 JavaScript。编译出来的 JavaScript 能够运行在任何浏览器上。TypeScript 编译工具能够运行在任何服务器和任何系统上编程
JavaScript 在过去几年中发展了不少。 它是用于客户端和服务器端的最通用的跨平台语言。数组
但 JavaScript 从未意味着进行如此大规模的应用程序开发。 它是一种没有类型系统的动态语言,这意味着变量能够具备任何类型的值,例如字符串或布尔值。浏览器
类型系统可提升代码质量,可读性,并使代码库的维护和重构更容易。 更重要的是,错误能够在编译时而不是在运行时捕获。安全
若是没有类型系统,很难扩展 JavaScript 以构建复杂的应用程序,而大型团队则使用相同的代码。服务器
TypeScript 在编译时提供代码不一样部分之间的保证。 编译器错误一般会告诉您确切的错误位置以及出现了什么问题,而运行时错误伴随着堆栈跟踪可能会产生误导并致使花费大量时间在调试工做上。前端工程师
const isLoading: boolean = false
复制代码
const decimal: number = 8
const binary: number = 0b110
复制代码
const fruit: string = 'orange'
复制代码
数组类型能够用如下两种方式之一编写:框架
// 常见的
let firstFivePrimes: number[] = [2, 3, 5, 7, 11]
// 不常见。 使用泛型类型(稍后会详细介绍)
let firstFivePrimes2: Array<number> = [2, 3, 5, 7, 11]
复制代码
数组合并了相同类型的对象,而元组(Tuple)合并了不一样类型的对象。元组起源于函数编程语言(如 F#),在这些语言中频繁使用元组。dom
let contact: [string, number] = ['John', 954683]
contact = ['Ana', 842903, 'extra argument'] /* Error! Type '[string, number, string]' is not assignable to type '[string, number]'. */
复制代码
any 与类型系统中的任何和全部类型兼容,这意味着能够将任何内容分配给它,而且能够将其分配给任何类型。它使您可以选择不经过类型检查。
let variable: any = 'a string';
variable = 5;
variable = false;
variable.someRandomMethod(); /_ Okay,
someRandomMethod might exist at runtime. _/
复制代码
void 是没有任何类型的。它一般用做不返回值的函数的返回类型。
function sayMyName(name: string): void {
console.log(name)
}
sayMyName('Heisenberg')
复制代码
never 类型表示从未发生的值的类型。例如,never 函数的返回类型将始终抛出异常或未达到其终点。
// throws an exception
function error(message: string): never {
throw new Error(message)
}
// unreachable end point
function continuousProcess(): never {
while (true) {
// ...
}
}
复制代码
可使用 null 和 undefined 来定义这两个原始数据类型,它们自己并非很是有用,可是当它们在联合类型中使用时会变得很是有用 (等下会有更多内容)
type someProp = string | null | undefined
复制代码
TypeScript 3.0 引入了未知类型,它是任何类型安全的对应类型。 任何东西均可以分配给未知的,可是未知的东西除了自己和任何东西外都不能分配。 若是没有先声明或缩小为更具体的类型,则不容许对未知操做进行操做。
type I1 = unknown & null // null
type I2 = unknown & string // string
type U1 = unknown | null // unknown
type U2 = unknown | string // unknown
复制代码
类型别名提供类型注释的名称,容许您在多个位置使用它。它们使用如下语法建立:
type Login = string
复制代码
TypeScript 容许咱们为属性使用多种数据类型。这称为联合类型。
type Password = string | number
复制代码
混合类型是组合全部成员类型的属性的类型
interface Person {
name: string
age: number
}
interface Worker {
companyId: string
}
type Employee = Person & Worker
const bestOfTheMonth: Employee = {
name: 'Peter',
age: 39,
companyId: '123456'
}
复制代码
在 TypeScript 中,咱们使用接口(Interfaces)来定义对象的类型。 在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动须要由类(classes)去实现(implement)。 TypeScript 中的接口是一个很是灵活的概念,除了可用于对类的一部分行为进行抽象之外,也经常使用于对「对象的形状(Shape)」进行描述。 附注:接口的运行时 JS 影响为零,它仅用于类型检查。
您能够声明标记带有?的可选属性,这意味着接口的对象可能定义这些属性,也可能不定义这些属性。 您能够声明只读属性,这意味着一旦为属性分配了值,就没法更改它。
interface ICircle {
readonly id: string
center: {
x: number
y: number
}
radius: number
color?: string // 可选属性
}
const circle1: ICircle = {
id: '001',
center: {
x: 0
},
radius: 8
}
/* Error! Property 'y' is missing in type '{ x: number; }' but required in type '{ x: number; y: number; }'. */
const circle2: ICircle = {
id: '002',
center: {
x: 0,
y: 0
},
radius: 8
} // Okay
circle2.color = '#666' // Okay
circle2.id = '003'
/* Error! Cannot assign to 'id' because it is a read-only property. */
复制代码
接口能够扩展一个或多个接口。这使得编写接口变得灵活且可重用。
interface ICircleWithArea extends ICircle {
getArea: () => number
}
const circle3: ICircleWithArea = {
id: '003',
center: { x: 0, y: 0 },
radius: 6,
color: '#fff',
getArea: function() {
return this.radius ** 2 * Math.PI
}
}
复制代码
实现接口的类须要严格遵循接口的结构。
interface IClock {
currentTime: Date
setTime(d: Date): void
}
class Clock implements IClock {
currentTime: Date = new Date()
setTime(d: Date) {
this.currentTime = d
}
constructor(h: number, m: number) {}
}
复制代码
一个 enum(或枚举)是组织相关的值,能够是数值或字符串值的集合的方式。
enum CardSuit {
Clubs,
Diamonds,
Hearts,
Spades
}
let card = CardSuit.Clubs
card = 'not a card suit' /* Error! Type '"not a card suit"' is not assignable to type 'CardSuit'. */
复制代码
Under the hood(不会翻译), 枚举默认状况下是基于数字的。enum 值从零开始,每一个成员递增 1。
咱们以前的示例生成的 JavaScript 代码:
var CardSuit
;(function(CardSuit) {
CardSuit[(CardSuit['Clubs'] = 0)] = 'Clubs'
CardSuit[(CardSuit['Diamonds'] = 1)] = 'Diamonds'
CardSuit[(CardSuit['Hearts'] = 2)] = 'Hearts'
CardSuit[(CardSuit['Spades'] = 3)] = 'Spades'
})(CardSuit || (CardSuit = {}))
/** * 这致使如下对象: * { * 0: "Clubs", * 1: "Diamonds", * 2: "Hearts", * 3: "Spades", * Clubs: 0, * Diamonds: 1, * Hearts: 2, * Spades: 3 * } */
复制代码
或者,可使用字符串值初始化枚举,这是一种更易读的方法。
enum SocialMedia {
Facebook = 'FACEBOOK',
Twitter = 'TWITTER',
Instagram = 'INSTAGRAM',
LinkedIn = 'LINKEDIN'
}
复制代码
enum 支持反向映射,这意味着咱们能够从其值中访问成员的值以及成员名称。 映射到咱们的 CardSuit 示例:
const clubsAsNumber: number = CardSuit.Clubs // 3
const clubsAsString: string = CardSuit[0] // 'Clubs'
复制代码
您能够为每一个参数添加类型,而后添加到函数自己以添加返回类型。
function add(x: number, y: number): number {
return x + y
}
复制代码
TypeScript 容许您声明函数重载。基本上,您可使用相同名称但不一样参数类型和返回类型的多个函数。请考虑如下示例:
function padding(a: number, b?: number, c?: number, d?: any) {
if (b === undefined && c === undefined && d === undefined) {
b = c = d = a
} else if (c === undefined && d === undefined) {
c = a
d = b
}
return {
top: a,
right: b,
bottom: c,
left: d
}
}
复制代码
每一个参数的含义根据传递给函数的参数数量而变化。并且,该函数只须要一个,两个或四个参数。要建立函数重载,只需屡次声明函数头。最后一个函数头是一个其实是活跃中的函数体,但不是提供给外面的世界。
function padding(all: number) function padding(topAndBottom: number, leftAndRight: number) function padding(top: number, right: number, bottom: number, left: number) function padding(a: number, b?: number, c?: number, d?: number) { if (b === undefined && c === undefined && d === undefined) { b = c = d = a } else if (c === undefined && d === undefined) { c = a d = b } return {
top: a,
right: b,
bottom: c,
left: d
}
}
padding(1) // Okay
padding(1, 1) // Okay
padding(1, 1, 1, 1) // Okay
padding(1, 1, 1) /* Error! No overload expects 3 arguments, but overloads do exist that expect either 2 or 4 arguments. */
复制代码
您能够向属性和方法的参数添加类型
class Greeter {
greeting: string
constructor(message: string) {
this.greeting = message
}
greet(name: string) {
return `Hi ${name}, ${this.greeting}`
}
}
复制代码
TypeScript 可使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected。 public 修饰的属性或方法是公有的,能够在任何地方被访问到,默认全部的属性和方法都是 public 的 private 修饰的属性或方法是私有的,不能在声明它的类的外部访问 protected 修饰的属性或方法是受保护的,它和 private 相似,区别是它在子类中也是容许被访问的
Accessible on | public | protected | private |
---|---|---|---|
class | yes | yes | yes |
class children | yes | yes | no |
class instance | yes | no | no |
A readonly property must be initialised at their declaration or in the constructor.
class Spider {
readonly name: string
readonly numberOfLegs: number = 8
constructor(theName: string) {
this.name = theName
}
}
复制代码
参数属性容许您在一个位置建立和初始化成员。它们经过在构造函数参数前加上修饰符来声明。
class Spider {
readonly numberOfLegs: number = 8
constructor(readonly name: string) {}
}
复制代码
abstract 关键字既可用于类,也可用于抽象类方法。
TypeScript 容许您以任何方式覆盖其推断类型。当您比变换器自己更好地理解变量类型时,可使用此方法。
const friend = {}
friend.name = 'John' // Error! Property 'name' does not exist on type '{}'
interface Person {
name: string
age: number
}
const person = {} as Person
person.name = 'John' // Okay
复制代码
最初类型断言的语法是<type>
let person = <Person>{}
复制代码
可是这在 JSX 中使用时产生了歧义。所以建议改成使用 as。
类型断言一般在从 JavaScript 迁移代码时使用,您可能知道变量的类型比当前分配的更准确。但断言可被视为有害。
咱们来看看上一个示例中的 Person 接口。你注意到了什么问题吗?若是您注意到失踪的房产年龄,恭喜!编译器可能会帮助您为 Person 的属性提供自动完成功能,但若是您错过任何属性,它将不会抱怨。
当没有类型注释形式的可用显式信息时,TypeScript 会推断变量类型。
/** * 变量定义 */
let a = 'some string'
let b = 1
a = b /** Error! Type 'number' is not assignable to type 'string'. **/
//若是是复杂对象,TypeScript会查找最多见的类型
//推断对象的类型。
const arr = [0, 1, false, true] // (number | boolean)[]
/** * 函数返回类型 */
function sum(x: number, y: number) {
return x + y // 推断返回一个数字
}
复制代码
类型兼容性基于结构类型,结构类型仅基于其成员关联类型。 结构类型的基本规则 x 是与 yif y 至少具备相同成员的兼容 x。
interface Person {
name: string
}
let x: Person // Okay, 尽管不是Person接口的实现
let y = { name: 'John', age: 20 } // type { name: string; age: number }
x = y
//请注意x仍然是Person类型。
//在如下示例中,编译器将显示错误消息,由于它不会
//指望在Person中的属性年龄,但结果将和预期的同样
console.log(x.age) // 20
复制代码
与 y 成员同样 name: string,它匹配 Person 接口的必需属性,这意味着它 x 是一个子类型 y。所以,容许分配。
在函数调用中,您须要传入至少足够的参数,这意味着额外的参数不会致使任何错误。
function consoleName(person: Person) {
console.log(person.name)
}
consoleName({ name: 'John' }) // Okay
consoleName({ name: 'John', age: 20 }) //也能够是额外的参数
复制代码
返回类型必须至少包含足够的数据。
let x = () => ({ name: 'John' })
let y = () => ({ name: 'John', age: 20 })
x = y // OK
y = x /* Error! Property 'age' is missing in type '{ name: string; }' but required in type '{ name: string; age: number; }' */
复制代码
类型守卫容许您缩小条件块中对象的类型。
在条件块中使用 typeof,编译器将知道变量的类型是不一样的。在下面的示例中,TypeScript 了解在条件块以外,x 多是布尔值,而且 toFixed 没法在其上调用该函数。
function example(x: number | boolean) {
if (typeof x === 'number') {
return x.toFixed(2)
}
return x.toFixed(2) // Error! Property 'toFixed' does not exist on type 'boolean'.
}
复制代码
class MyResponse {
header = 'header example'
result = 'result example'
// ...
}
class MyError {
header = 'header example'
message = 'message example'
// ...
}
function example(x: MyResponse | MyError) {
if (x instanceof MyResponse) {
console.log(x.message) // Error! Property 'message' does not exist on type 'MyResponse'.
console.log(x.result) // Okay
} else {
// TypeScript knows this must be MyError
console.log(x.message) // Okay
console.log(x.result) // Error! Property 'result' does not exist on type 'MyError'.
}
}
复制代码
该 in 操做员检查的对象上的属性的存在。
interface Person {
name: string
age: number
}
const person: Person = {
name: 'John',
age: 28
}
const checkForName = 'name' in person // true
复制代码
文字是精确值,是 JavaScript 原语。它们能够组合在一个类型联合中以建立有用的抽象。
type Orientation = 'landscape' | 'portrait'
function changeOrientation(x: Orientation) {
// ...
}
changeOrientation('portrait') // Okay
changeOrientation('vertical') /* Error! Argument of type '"vertical"' is not assignable to parameter of type 'Orientation'. */
复制代码
条件类型描述类型关系测试,并根据该测试的结果选择两种可能类型中的一种。
type X = A extends B ? C : D
复制代码
这意味着若是 type A 能够赋值给 type B,那么 X 它的类型是 C。不然 X 与类型相同 D;
通用类型是必须包含或引用其余类型才能完成的类型。它强制执行各类变量之间的有意义约束。 在如下示例中,函数返回您传入的任何类型的数组。
function reverse<T>(items: T[]): T[] {
return items.reverse()
}
reverse([1, 2, 3]) // number[]
reverse([0, true]) // (number | boolean)[]
复制代码
该 keyof 运营商查询组给定类型的钥匙。
interface Person {
name: string
age: number
}
type PersonKeys = keyof Person // 'name' | 'age'
复制代码
映射类型容许您经过映射属性类型从现有类型建立新类型。根据您指定的规则转换现有类型的每一个属性。
type Partial<T> = { [P in keyof T]?: T[P] }
复制代码
正如咱们在 Interface 部分中所介绍的,TypeScript 容许您建立只读属性。有一种 Readonly 类型,它接受一个类型 T 并将其全部属性设置为只读。
type Readonly<T> = { readonly [P in keyof T]: T[P] }
复制代码
Exclude 容许您从其余类型中删除某些类型。Exclude 来自 T 任何可分配的东西 T。
/** * type Exclude<T, U> = T extends U ? never : T; */
type User = {
_id: number
name: string
email: string
created: number
}
type UserNoMeta = Exclude<keyof User, '_id' | 'created'>
复制代码
Pick 容许您从其余类型中选择某些类型。Pick 来自 T 任何可分配的东西 T。
/** * type Pick<T, K extends keyof T> = { * [P in K]: T[P]; * }; */
type UserNoMeta = Pick<User, 'name' | 'email'>
复制代码
您可使用 infer 关键字在 extends 条件类型的子句中推断类型变量。此类推断类型变量只能用于条件类型的 true 分支。
获取函数的返回类型。
/** * 原始TypeScript的ReturnType * type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; */
type MyReturnType<T> = T extends (...args: any) => infer R ? R : any
type TypeFromInfer = MyReturnType<() => number> // number
type TypeFromFallback = MyReturnType<string> // any
复制代码
分析一下 MyReturnType:
原文:www.freecodecamp.org/news/the-de…
参考:ts.xcatliu.com/ 翻译不许确,或者难以理解的地方你们能够指出,万分感谢