TypeScript 是 JavaScript 的超集,具有静态类型特性,旨在简化大型 JavaScript 应用程序的开发,也被称为 JavaScript that scales(可拓展的 JavaScript)。
JavaScript 在过去几年中快速发展,成为客户端和服务器端最通用的跨平台语言。
但 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 类型表示一种组织好的数组,元素的类型预先知道,并且数量固定。这意味着你有可能得到错误提示:
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(); /* 行吧,
也许运行的时候 someRandomMethod 是存在的 */
void 表示没有任何类型。它通常用作没有返回值的函数的返回类型。
function sayMyName(name: string): void {
console.log(name);
}
sayMyName('Heisenberg');
never 类型表示的是那些永不存在的值的类型。 例如,never 类型是那些总是会抛出异常、或者根本就不会有返回值的函数的返回值类型。
// 抛出异常
function error(message: string): never {
throw new Error(message);
}
// 永远不能返回
function continuousProcess(): never {
while (true) {
// ...
}
}
undefined 和 null 两者各自有自己的类型分别叫做 undefined 和 null。和 void 相似,它们的本身的类型用处不是很大,但是在联合类型中非常有用 (稍后介绍)。
type someProp = string | null | undefined;
TypeScript 3.0 引入了 unknown (未知) 类型,它是与 any 类型对应的安全类型。任何东西都可以赋值给 unknown,但 unknown 不能赋值给除了它本身和 any 以外的任何东西。在没有先断言或指定到更具体类型的情况下,不允许对 unknown 进行任何操作。
type I1 = unknown & null; // null
type I2 = unknown & string; // string
type U1 = unknown | null; // unknown
type U2 = unknown | string; // unknown
类型别名可以为现有类型提供替代名称,以便某些地方使用。构造它的语法如下:
type Login = string;
TypeScript 允许让一个属性具有多种数据类型,名为 union (联合) 类型。
type Password = string | number;
交叉类型是将多种类型叠加到一起成为一种类型。
interface Person {
name: string;
age: number;
}
interface Worker {
companyId: string;
}
type Employee = Person & Worker;
接口好似你和编译器定义契约,由你指定一个类型,预期它的属性应该是些什么类型。
边注:接口不受 JavaScript 运行时的特性影响,它只在类型检查中会用到。
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,
} // 正确
接口可以扩展成另一个接口,或者更多接口。这使得接口的编写更具有灵活性和复用性。
interface ICircleWithArea extends ICircle {
getArea: () => number;
}
实现接口的类需要严格遵循接口的结构。
interface IClock {
currentTime: Date;
setTime(d: Date): void;
}
enum (枚举) 用来组织一组的相关值,这些值可以是数值,也可以是字符串值。
enum CardSuit {
Clubs,
Diamonds,
Hearts,
Spades
}
let card = CardSuit.Clubs;
默认情况下,枚举的本质是数字。enum 的取值从 0 开始,以 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 = {}));
或者,枚举可以用字符串值来初始化,这种方法更易读。
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
};
}
你可以指定属性的类型和方法参数的类型。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet(name: string) {
return `Hi ${name}, ${this.greeting}`;
}
}
Typescript 支持 public (公有), private (私有), protected (保护) 修饰符,它们决定了类成员的可访问性。
| 具有访问权限 | public | protected | private |
| :------------- | :----: | :-------: | :-----: |
| 类本身 | yes | yes | yes |
| 派生类 | yes | yes | no |
| 类实例 | yes | no | no |
readonly (只读) 变量必须在它声明或构造时初始化。
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'; // 正确
最初,类型断言的语法是 <type>
let person = <Person> {};
但这在 JSX 中使用时产生了歧义。因此建议使用 as 代替。
类型断言通常在从 JavaScript 迁移代码时使用,你对变量的类型了解可能比当前指派的更准确。
但断言也会 被认为有害。
我们来看看上一个示例中的 Person 接口,你注意到了什么问题吗?如果你注意到丢失了 age 属性,恭喜,你对了!编译器可能会帮助你自动完成 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)[]
类型兼容性是基于结构类型的,结构类型只使用其成员来描述类型。
结构化类型系统的基本规则是:如果 x 要兼容 y,那么 y 至少具有与 x 相同的属性。
interface Person {
name: string;
}
let x: Person; // 正确,尽管不是Person接口的实现
let y = { name: 'John', age: 20 }; // type { name: string; age: number }
x = y;
由于 y 有一个成员 name: string 匹配 Person 接口所需的属性,这意味着 x 是 y 的子类型。因此这个赋值是合法的。
参数数量
在函数调用中,至少需要传入足够的参数,多余的参数不会导致任何错误。
function consoleName(person: Person) {
console.log(person.name);
}
consoleName({ name: 'John' }); // 正确
consoleName({ name: 'John', age: 20 }); // 多余的参数也合法
返回值类型
返回值类型必须至少包含足够的数据。
let x = () => ({name: 'John'});
let y = () => ({name: 'John', age: 20 });
x = y; // 正确
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); // 正确
} else {
// TypeScript 知道这里一定是 MyError
console.log(x.message); // 正确
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 原始数据类型具体的值,它们可以与 union (联合) 类型搭配使用,构造一些实用的概念。
type Orientation = 'landscape' | 'portrait';
function changeOrientation(x: Orientation) {
// ...
}
changeOrientation('portrait'); // 正确
changeOrientation('vertical'); /* Error! Argument of type '"vertical"' is not
assignable to parameter of type 'Orientation'. /
条件类型表示类型关系的测试,并根据测试的结果选择两种可能类型中的一种。
type X = A extends B ? C : D;
如果 A 类型可以赋值给 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];
}
正如在接口部分中所介绍的,TypeScript 中可以创建只读属性。 Readonly 类型接受一个类型T,并将其所有属性设置为只读。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
Exclude 可以从其他类型中排除某些类型。排除的是可以赋值给 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 可以从其他类型中选取某些类型。 挑选的是可以赋值给 T 的属性。
/**
* type Pick<T, K extends keyof T> = {
* [P in K]: T[P];
* };
*/
type UserNoMeta = Pick<User, 'name' | 'email'>
你可以使用 infer 关键字来推断条件类型的 extends 子句中的类型变量。这样的推断类型变量只能用于条件类型的 true 分支。
获取函数的返回类型。
/**
* 原版的 TypeScript's 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:
原文:https://zhuanlan.zhihu.com/p/83689446
近些日子,我使用了新语言编程,从JavaScript,切确地说是Elm,转成TypeScript。在本文中,我将继续深挖一些我非常喜欢的TypeScript特性。
TypeScript 和 JavaScript 是目前项目开发中较为流行的两种脚本语言,我们已经熟知 TypeScript 是 JavaScript 的一个超集,但是 TypeScript 与 JavaScript 之间又有什么样的区别呢?
Nerv_是一款由京东凹凸实验室打造的类 React 前端框架,基于虚拟 DOM 技术的 JavaScript(TypeScript) 库。它基于React标准,提供了与 React 16 一致的使用方式与 API。
交叉类型:将多个类型合并为一个类型、联合类型:表示取值可以为多种类型中的一种、混合类型:一个例子就是,一个对象可以同时做为函数和对象使用,并带有额外的属性、类型断言:可以用来手动指定一个值的类型
在做比较大的,多人合作的项目的时候,TypeScript会更加地适合,这得益于它的可读性,面向对象性以及易于重构的特点。但如果只是自己做小程序,不需要太多人参与的时候,JavaScript则会更加简单。
有两种方式安装TypeScript,如何创建第一个TypeScript文件,在TypeScript中,可以使用interface来描述一个对象有firstName和lastName两个属性,TypeScript支持JavaScript的新功能,其中很重要的一个功能就是基于类的面向对象编程
使用TypeScript已经有了一段时间,这的确是一个好东西,虽说在使用的过程中也发现了一些bug,不过都是些小问题,所以整体体验还是很不错的。有关TypeScript声明类型声明相关的目前就总结了这些比较常用的
谷歌在很早之前就张开双臂拥抱 Web 应用程序,Gmail 已经发布 14 年了。当时,JavaScript 的世界是疯狂的。Gmail 工程师不得不为 IE 糟糕的垃圾回收算法捏一把汗,他们需要手动将字符串文字从 for 循环中提取出来,以避免 GC 停顿
TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个超集,TypeScript是JavaScript类型的超集,它可以编译成纯JavaScript。TypeScript可以在任何浏览器、任何计算机和任何操作系统上运行,并且是开源的。
差不多两年前,我在一个创业团队中开始了一个全新的项目。用到的全都是类似Microservices,docker,react,redux这些时髦的东西。我在前端技术方面积累了一些类似的经验
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!