资讯专栏INFORMATION COLUMN

一篇文章学会 TypeScript

itvincent / 2297人阅读

摘要:接下来来看一段代码示例语法与语言比较当类型不对的时候,会提示错误编译后语法联想大致可以把它看成是加了类型系统的。

一篇文章学会 TypeScript (内部分享标题:TypeScript 基础)
这篇文章是我在公司前端小组内部的演讲分享稿,目的是教会大家使用 TypeScript,
这篇文章虽然标着基础,但我指的基础是学完后就能够胜任 TypeScript 的开发工作。
从我分享完的效果来看,大家都学会了,效果还是好的。我把这篇分享稿发布出来,
希望能对大家的 TypeScript 学习有所帮助。
1、什么是 TypeScript?

TypeScript 是由微软发布的一款开源的编程语言。它是 JavaScript 的超集(兼容 JavaScript 代码),其代码必须经过编译后,方可在 JavaScript 环境中运行,核心功能是类型系统可以提前使用 ES 的新特性

接下来来看一段代码示例:

// 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 会提示错误:

编译后:

// JavaScript 语法
function add(x, y) {
    const res = x + y;
    return res;
}

联想:大致可以把它看成是加了类型系统的 Babel。

问题:为什么要学习 TypeScript?

类型系统可以在代码编译阶段就可以发现潜在的变量类型问题,甚至在开发阶段,IDE 就可以帮助我们找到这些潜在的问题,避免了项目在上线后才发现变量类型不对的错误。

TypeScript 是前端开发的趋势,有些特性还可能会成为 ES 的新标准,Vue 3.0 也会采用 TypeScript 作为开发语言,当你想看源码的时候不至于看不懂,跟着趋势走肯定是没错的。

减少类型校验等无用的代码书写。

可以提前享受 ES 新特性带来的便利。

向 qian 看,qian 途。

2、快速上手,三步走 第一步:全局安装 TypeScript
$ yarn global add typescript

// 测试是否安装成功
$ tsc -v
Version 3.4.5
第二步:创建 .ts 文件
// 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;
文件监听,实时编译

适用于边开发边看结果的情况:

$ tsc -w add.ts
Starting compilation in watch mode...

Watching for file changes.
3、TypeScript 的基本类型
// 布尔值
let isDone: boolean = false;
// 数值
let age: number = 12;
// 字符串
let name: string = "Jay";
// 数组
let list: number[] = [1, 2, 3];
let list: Array = [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;
4、类和接口

类是对属性和方法的封装

类的示例:

// 跟 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);
protected、private、readonly 和 super

protected 和 private 都不能从外部访问

protected 可以被继承,private 不可以

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};
接口

接口是用来定义规范的,常常用作类型说明。
接口的示例:

// 定义规范,限定类型
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) { }
}
5、type、 namespace 和泛型 type 定义新类型

当 TypeScript 提供的类型不够用时,可以用来自定义类型,供自己使用。

// type 定义新类型,相当于类型别名
type myType = string | number | boolean
// 使用:
const foo: myType = "foo"

// type 定义函数类型
type hello = (msg: string) => void

// type 对象类型
type WebSite = {
    url: string;
    title: number;
}
namespace 命名空间

命名空间主要有两个方面的用途:

组织代码。组织一些具有内在联系的特性和对象,能够使代码更加清晰。

避免名称冲突。

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;
  }
}
使用外部定义的 namespace
/// 
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(value: T): T {
    return value;
}
// 使用
middleware(service.getStringValue());
middleware(service.getNumberValue());

更多内容查看文章:泛型

6、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 方法的接口文档:

手动创建声明文件

有以下两个场景会去手动创建这个文件:

给未使用 TypeScript 的 js 模块编写声明文件,可以当做交接文档用;使用别人没有写声明文件的 js 库。

模块补充。

// 定义模块补充的语法
declare module "filePath" {
   
}

module-augmentation

使用场景:vue-shim.d.ts 文件,为了让 TypeScript 识别 .vue 文件。

// 文件内容
declare module "*.vue" {
    import Vue from "vue";
    export default Vue;
}

增强类型以配合插件使用

7、配置文件 冗长的参数

指定输出目录:

$ tsc -d --strict -m ESNext --outDir lib index.ts

tsc 的所有参数可以通过执行:tsc -h来查看。

使用 tsconfig.json 配置文件

避免书写冗长的命令,丰富的配置项。

生成 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
8、使用 TypeScript 开发 web 应用

上述的方法适用于开发命令行工具,比如 @xiyun/cli,开发 web 应用还是不太方便。

如果想要方便地开发 web 应用,比如:开启前端服务、模块热加载等,就需要配合构建工具使用。

配合 webpack 开发 web 应用

点击查看配合 webpack 开发 web 应用源码

配合 parcel,零配置开发 web 应用

parcel:极速零配置 Web 应用打包工具。

联想:可以看成是已经配置好了的 webpack。

全局安装 parcel 工具:

$ yarn global add parcel-bundler

运行:

$ parcel index.html

它就会帮你生成 package.json,自动安装 typeScript,启动好开发服务,并支持热加载。

9、在 Vue 和 React 项目中使用 让 Vue 支持 TypeScript

最方便快捷:vue create my-app,选择 TypeScript。

如果要改造现有项目:

增加 dev 依赖包:

"devDependencies": {
    "@vue/cli-plugin-typescript": "^3.8.0",
    "typescript": "^3.4.3",
  }

增加 tsconfig.json 配置

更改文件后缀:main.js --> main.ts

组件中指定 lang:

在 src 目录下加上:vue-shim.d.ts 文件,为了让 TypeScript 识别 .vue 文件。

// 文件内容
declare module "*.vue" {
    import Vue from "vue";
    export default Vue;
}

Vue 支持 TypeScript 的配置参考

另外一种语法

如果想要用这种语法来写 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"
  },

Vue 官方对 TypeScript 的说明

创建支持 TypeScript 的 React 项目

创建应用时加上--typescript参数即可:

$ create-react-app my-app --typescript
参考资料

TypeScript 官方文档
TypeScript Declare Keyword
Vue 对 模块补充的说明

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104318.html

相关文章

  • 前端周刊第59期:选 Flow 还是 TypeScript

    摘要:周末是时隔两月的家人团聚,而每次内容的准备平均需要我集中精力工作小时,所以第期的内容今早才准备好,对不住大家了。下面是本周精选内容,请享用。本文作者王仕军,商业转载请联系作者获得授权,非商业转载请注明出处。 showImg(https://segmentfault.com/img/remote/1460000009742537?w=1240&h=509); 周末是时隔两月的家人团聚,而...

    Julylovin 评论0 收藏0
  • 前端常用技术概述--Less、typescript与webpack

    摘要:前言讲起前端,我们就不能不讲与,在这两种技术广泛应用的今天,他们的扩展也是层出不穷,的扩展有等,的超集有等。如下注意你的样式文件一定要在引入前先引入。截止目前,已经发布了最新的版本。 前言:讲起前端,我们就不能不讲CSS与Javascript,在这两种技术广泛应用的今天,他们的扩展也是层出不穷,css的扩展有Less、Sass、Stylus等,js的超集有Typescript等。今天我们就简...

    番茄西红柿 评论0 收藏0
  • typescript开发手势库 - (1)web开发常用手势有哪些?

    这只是个开头 说在最前面,本文是一个系列文章的开头, 这个系列里我会讲如何用typescript开发一款支持pc和手机端的手势库any-touch, 以及通过jest让你的代码测试覆盖率100%. showImg(https://segmentfault.com/img/bVbp3B0?w=936&h=246); 目录 用TypeScript开发手势库 - (2)tsconfig.json & r...

    raise_yang 评论0 收藏0
  • 前端进阶资源整理

    摘要:前端进阶进阶构建项目一配置最佳实践状态管理之痛点分析与改良开发中所谓状态浅析从时间旅行的乌托邦,看状态管理的设计误区使用更好地处理数据爱彼迎房源详情页中的性能优化从零开始,在中构建时间旅行式调试用轻松管理复杂状态如何把业务逻辑这个故事讲好和 前端进阶 webpack webpack进阶构建项目(一) Webpack 4 配置最佳实践 react Redux状态管理之痛点、分析与...

    BlackMass 评论0 收藏0
  • 平时积累的前端资源,持续更新中。。。

    本文收集学习过程中使用到的资源。 持续更新中…… 项目地址 https://github.com/abc-club/f... 目录 vue react react-native Weex typescript Taro nodejs 常用库 css js es6 移动端 微信公众号 小程序 webpack GraphQL 性能与监控 高质文章 趋势 动效 数据结构与算法 js core 代码规范...

    acrazing 评论0 收藏0

发表评论

0条评论

itvincent

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<