这篇文章是我在公司前端小组内部的演讲分享稿,目的是教会你们使用 TypeScript,
这篇文章虽然标着基础,但我指的基础是学完后就可以胜任 TypeScript 的开发工做。
从我分享完的效果来看,你们都学会了,效果仍是好的。我把这篇分享稿发布出来,
但愿能对你们的 TypeScript 学习有所帮助。
TypeScript 是由微软发布的一款开源的编程语言。它是 JavaScript 的超集(兼容 JavaScript 代码),其代码必须通过编译后,方可在 JavaScript 环境中运行,核心功能是类型系统和能够提早使用 ES 的新特性。javascript
接下来来看一段代码示例:html
// TypeScript 语法 function add(x: number, y: number): number { const res: number = x + y; return res; } // 与 C 语言比较 int add(int x, int y) { int res = x + y; return res; }
当类型不对的时候,IDE 会提示错误:前端
编译后:vue
// JavaScript 语法 function add(x, y) { const res = x + y; return res; }
联想:大体能够把它当作是加了类型系统的 Babel。java
$ yarn global add typescript // 测试是否安装成功 $ tsc -v Version 3.4.5
// add.ts function add(x: number, y: number): number { const res: number = x + y; return res; } export default add;
$ tsc add.ts
// add.js function add(x, y) { const res = x + y; return res; } export default add;
适用于边开发边看结果的状况:react
$ tsc -w add.ts Starting compilation in watch mode... Watching for file changes.
// 布尔值 let isDone: boolean = false; // 数值 let age: number = 12; // 字符串 let name: string = 'Jay'; // 数组 let list: number[] = [1, 2, 3]; let list: Array<number> = [1, 2, 3]; // 对象 let mokey: object = {name: 'wu kong'}; // 空值,表示没有返回值 function print(): void {} // 任意值 let goods: any = {}; let goods: any = 2019; // 未指定类型,视为任意值 let goods; // 类型推断 let name = 'Jay'; name = 123; // 报错 // 联合类型 let name: string | number; name = 'Jay'; name = 123;
类是对属性和方法的封装webpack
类的示例:git
// 跟 JavaScript 的类类似,多了访问控制等关键词 class Monkey { public height: number; private age: number = 12; public static mkName: string = 'kinkong'; private static action: string = 'jump'; constructor(height: number) { this.height = height; } public getAge(): number { return this.age; } } const monkey = new Monkey(120); monkey.getAge(); const height = monkey.height; const age = monkey.age; // 报错 const mkName = Monkey.mkName; const action = Monkey.action; // 报错
class Animal { move(distanceInMeters: number = 0) { console.log(`Animal moved ${distanceInMeters}m.`); } } class Dog extends Animal { bark() { console.log('Woof! Woof!'); } } const dog = new Dog(); dog.bark(); dog.move(10);
class Person { protected name: string; private age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } } class Employee extends Person { private readonly initial: string = 'abc'; private readonly department: string; constructor(name: string, department: string) { super(name, 1); this.department = department; } public getElevatorPitch() { return `Hello, my name is ${this.name} and I work in ${this.department}.`; } public getAge() { return this.age; } } let howard = new Employee("Howard", "Sales"); console.log(howard.getElevatorPitch()); console.log(howard.name); // error
let passcode = "secret passcode"; class Employee { private _fullName: string; get fullName(): string { return this._fullName; } set fullName(newName: string) { if (passcode && passcode == "secret passcode") { this._fullName = newName; } else { console.log("Error: Unauthorized update of employee!"); } } } let employee = new Employee(); employee.fullName = "Bob Smith"; if (employee.fullName) { console.log(employee.fullName); }
class Point { x: number; y: number; } interface Point3d extends Point { z: number; } let point3d: Point3d = {x: 1, y: 2, z: 3};
接口是用来定义规范的,经常用做类型说明。
接口的示例:github
// 定义规范,限定类型 interface Point { x: number; y: number; // 可选属性 z?: number; } let pt: Ponit = {x: 1, y: 1, z: 1}; function setPoint(point: Point): void {} setPoint({x: 1, y: 1}); // 实现接口 class point implements Point { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } public getPoint(): object { return { x: this.x, y: this.y, } } } const p = new point(3, 4); p.getPoint();
interface Shape { color: string; } interface Square extends Shape { sideLength: number; }
interface ClockConstructor { new (hour: number, minute: number); } class Clock implements ClockConstructor { currentTime: Date; constructor(h: number, m: number) { } }
当 TypeScript 提供的类型不够用时,能够用来自定义类型,供本身使用。web
// type 定义新类型,至关于类型别名 type myType = string | number | boolean // 使用: const foo: myType = 'foo' // type 定义函数类型 type hello = (msg: string) => void // type 对象类型 type WebSite = { url: string; title: number; }
命名空间主要有两个方面的用途:
namespace Validator { export interface StringValidator { isAcceptable(s: string): boolean; } const lettersRegexp = /^[A-Za-z]+$/; const numberRegexp = /^[0-9]+$/; // 当声明一个命名空间的时候,全部实体部分默认是私有的,可使用 export 关键字导出公共部分。 export class LettersOnlyValidator implements StringValidator { isAcceptable(s: string) { return lettersRegexp.test(s); } } export class ZipCodeValidator implements StringValidator { isAcceptable(s: string) { return s.length === 5 && numberRegexp.test(s); } } } new Validator.LettersOnlyValidator(); interface MyClassMethodOptions { name?: string; age?: number; } namespace MyClass { export interface MyClassMethodOptions { width?: number; height?: number; } }
/// <reference path="validate.ts" /> const v = new Validator.LettersOnlyValidator();
泛型容许在强类型程序设计语言中编写代码时,使用一些之后才指定的类型,在实际调用时才去指定泛型的类型。
举例:
// 模拟服务,提供不一样的数据。这里模拟了一个字符串和一个数值 const service = { getStringValue: function() { return "a string value"; }, getNumberValue: function() { return 20; } }; // 处理数据的中间件 function middleware(value: string): string { return value; } // 没问题 middleware(service.getStringValue()); // 报错:参数类型不对 middleware(service.getNumberValue()); // 设成 any 行不行? function middleware(value: any): any // 多写几个 middleware 行不行? function middleware1(value: string): string { ... } function middleware2(value: number): number { ... } // 改成泛型 function middleware<T>(value: T): T { return value; } // 使用 middleware<string>(service.getStringValue()); middleware<number>(service.getNumberValue());
xxx.d.ts
声明文件若是把 add.js 发布成一个 npm 包,别人在使用的时候 IDE 的提示每每都不太友好:
那么如何能让 IDE 给 add 方法加上类型提示和参数提示呢?
TypeScript 提供了 xxx.d.ts
文件来给 IDE 使用。
xxx.d.ts
叫作声明文件,通常用于类型提示和模块补充,能够自动生成,也能够手动编写,通常状况下是不用咱们手动编写的。
// 加上 -d 参数,就会生成 add.d.ts 文件 $ tsc -d add.ts
// add.d.ts declare function add(x: number, y: number): number; export default add;
declare 关键字
用于声明你须要的变量、函数、类等,声明之后 IDE 就能够根据它来进行类型提示。
若是有 add.d.ts 文件,那么它就承担了类型提示的任务:
同时也至关于 add 方法的接口文档:
有如下两个场景会去手动建立这个文件:
// 定义模块补充的语法 declare module 'filePath' { }
使用场景:vue-shim.d.ts 文件,为了让 TypeScript 识别 .vue 文件。
// 文件内容 declare module "*.vue" { import Vue from "vue"; export default Vue; }
指定输出目录:
$ tsc -d --strict -m ESNext --outDir lib index.ts
tsc 的全部参数能够经过执行:tsc -h
来查看。
避免书写冗长的命令,丰富的配置项。
生成 tsconfig.json 文件:
$ tsc --init
经常使用配置项解读:
{ "compilerOptions": { "target": "ES5", /* target用于指定编译以后的版本目标 version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ "module": "ESNext", /* 用来指定要使用的模块标准: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "declaration": true, /* 生成对应的 '.d.ts' 声明文件 */ "outDir": "./lib", /* 指定编译输出目录 */ "esModuleInterop": true, /* 经过为导入内容建立命名空间,实现CommonJS和ES模块之间的互操做性 */ "experimentalDecorators": true, /* 启用 ES7 装饰器语法 */ } }
配置后,执行如下命令,就会读取配置文件:
$ tsc add.ts
上述的方法适用于开发命令行工具,好比 @xiyun/cli,开发 web 应用仍是不太方便。
若是想要方便地开发 web 应用,好比:开启前端服务、模块热加载等,就须要配合构建工具使用。
parcel:极速零配置 Web 应用打包工具。
联想:能够当作是已经配置好了的 webpack。
全局安装 parcel 工具:
$ yarn global add parcel-bundler
运行:
$ parcel index.html
它就会帮你生成 package.json,自动安装 typeScript,启动好开发服务,并支持热加载。
最方便快捷:vue create my-app
,选择 TypeScript。
若是要改造现有项目:
"devDependencies": { "@vue/cli-plugin-typescript": "^3.8.0", "typescript": "^3.4.3", }
main.js --> main.ts
<script lang="ts"> </script>
// 文件内容 declare module "*.vue" { import Vue from "vue"; export default Vue; }
若是想要用这种语法来写 Vue 组件:
import { Component, Prop, Vue } from 'vue-property-decorator'; @Component export default class HelloWorld extends Vue { @Prop() private msg!: string; }
那么须要加上这两个依赖:
"dependencies": { // 官网出的ts支持包 "vue-class-component": "^7.0.2", // 对ts支持包更好封装的装饰器 "vue-property-decorator": "^8.1.0" },
建立应用时加上--typescript
参数便可:
$ create-react-app my-app --typescript