Typescript 语言简介, 以学习 React 开发.
有如下两种方式可以执行 ts 文件:
- 用 ts-node 来执行相关的文件.
- 用 npm 初始化, 然后 npm start 的方式.
基本数据类型
- boolean
- number
- bigint
- array
- tuple
- enum
- any
- void
- undefined
- null
- never
特殊类型: object, 推测在 ts 中表示可选类型是这样的: (可空的类型 | null)
类型转换
使用 as 进行数据类型转换.
接口
interface LabeledObject { label: string } function printLabelWith(obj: LabeledObject) { console.log(obj.label) }
接口可以定义可选的属性.
类
类的简单定义如下所示:
class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; } } let greeter = new Greeter("world");
其中构造方法使用 constructor, 另外方法不需要添加 function 来修饰. 在 ts 中新建对象仍然使用 new 关键字.
继承的写法如下:
class Animal { move(distanceInMeters: number = 0) { console.log(`Animal moved ${distanceInMeters}m.`); } } class Dog extends Animal { bark() { console.log('Woof! Woof!'); } }
另外方法的覆盖直接写同名方法即可, 不需要添加 override, 这个和 oc 的语法类似:
class Animal { name: string; constructor(theName: string) { this.name = theName; } move(distanceInMeters: number = 0) { console.log(`${this.name} moved ${distanceInMeters}m.`); } } class Snake extends Animal { constructor(name: string) { super(name); } move(distanceInMeters = 5) { console.log("Slithering..."); super.move(distanceInMeters); } } class Horse extends Animal { constructor(name: string) { super(name); } move(distanceInMeters = 45) { console.log("Galloping..."); super.move(distanceInMeters); } }
属性或方法如果没有添加访问修饰, 则默认是 public 的, 在 ts 中有 public/private/protected 三种访问修饰.
在类中仍然可以拥有 static 属性.
在 ts 中可以声明抽象类, 使用 abstract 语法.
在 ts 中还可以使用 class 作为接口的父接口使用, 这个类似 dart 中使用 class 作为接口的用法:
class Point { x: number; y: number; } interface Point3d extends Point { z: number; } let point3d: Point3d = {x: 1, y: 2, z: 3};
泛型
泛型工厂方法的实现: 居然可以这样来避免外部直接使用 new, 而且是泛型的
function create<T>(c: {new(): T; }): T { return new c(); }
更复杂的例子可以是这样的:
class BeeKeeper { hasMask: boolean; } class ZooKeeper { nametag: string; } class Animal { numLegs: number; } class Bee extends Animal { keeper: BeeKeeper; } class Lion extends Animal { keeper: ZooKeeper; } function createInstance<A extends Animal>(c: new () => A): A { return new c(); } createInstance(Lion).keeper.nametag; // typechecks! createInstance(Bee).keeper.hasMask; // typechecks!
模块(Module)
模块是独立的程序单元, 在其中的类, 对象, 变量不会被暴露在全局范围, 除非使用 export 显式暴露. 使用者通过 import 语法来使用被 export 的内容.
在 TS/JS 中, 如果顶层包含 import/export 的文件, 就认为是一个 module.
不包含 import/export 的就认为是脚本, 它里面的内容是全局的.
export 的语法:
export const numberRegexp = /^[0-9]+$/; export class ZipCodeValidator implements StringValidator { isAcceptable(s: string) { return s.length === 5 && numberRegexp.test(s); } } // 如果要重命名, 可以使用下面的语法: class ZipCodeValidator implements StringValidator { isAcceptable(s: string) { return s.length === 5 && numberRegexp.test(s); } } export { ZipCodeValidator }; export { ZipCodeValidator as mainValidator }; // 通过某个模块暴露其它模块的内容(类似一个汇总的 header), 可以像下面这样: export class ParseIntBasedZipCodeValidator { isAcceptable(s: string) { return s.length === 5 && parseInt(s).toString() === s; } } // Export original validator but rename it export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator"; // 或者是直接 export 另外一个模块的全部内容, 也是相当于汇总的: export * from "./StringValidator"; // exports 'StringValidator' interface export * from "./ZipCodeValidator"; // exports 'ZipCodeValidator' and const 'numberRegexp' class export * from "./ParseIntBasedZipCodeValidator";
import 的语法:
// 引入单一实体: import { ZipCodeValidator } from "./ZipCodeValidator"; let myValidator = new ZipCodeValidator(); // 在引入后重命名: import { ZipCodeValidator as ZCV } from "./ZipCodeValidator"; let myValidator = new ZCV(); // 从一个模块中引入所有内容, 作为一个变量使用: import * as validator from "./ZipCodeValidator"; let myValidator = new validator.ZipCodeValidator(); // 比如 React 中引入某个 css 文件, 以便实现一些副作用, 可以这样: import "./my.css";
另外每个模块都可以包含唯一的 export default:
export default class ZipCodeValidator { static numberRegexp = /^[0-9]+$/; isAcceptable(s: string) { return s.length === 5 && ZipCodeValidator.numberRegexp.test(s); } } import validator from "./ZipCodeValidator"; // 默认引入的就是 default 的那个. let myValidator = new validator();
名字空间
在 ts 中可以使用和 cs 类似的名字空间的概念. 通过名字空间来组织代码, 另外还可以将一个名字空间分布到多个文件中, 只需要添加 reference tag:
Validation.ts:
namespace Validation { export interface StringValidator { isAcceptable(s: string): boolean; } }
LettersOnlyValidator.ts:
/// <reference path="Validation.ts" /> namespace Validation { const lettersRegexp = /^[A-Za-z]+$/; export class LettersOnlyValidator implements StringValidator { isAcceptable(s: string) { return lettersRegexp.test(s); } } }
ZipCodeValidator.ts:
/// <reference path="Validation.ts" /> namespace Validation { const numberRegexp = /^[0-9]+$/; export class ZipCodeValidator implements StringValidator { isAcceptable(s: string) { return s.length === 5 && numberRegexp.test(s); } } }
需要认识到名字空间和 module 是两种不同的模块组织形式.