前端初探——typescript学习整理

typescript

  • 补充
      • TS的脑图
      • VSCode实现用Ctrl+滚轮实现代码的缩放
      • 格式化代码
      • 打断点
  • 1.基础类型
      • 类型断言
  • 2.接口
      • 普通接口
    • readonly vs const
      • 函数类型
      • 可索引的类型
      • 类类型
      • 混合类型
      • 接口继承类
  • 3.类
      • readonly修饰符
      • 存取器
  • 4.函数( 转demo3)
      • 为函数的参数和返回值添加类型
      • 重载
  • 5.泛型
      • 泛型接口
      • 泛型类
  • 6.枚举
      • 数字枚举
      • 字符串枚举
      • 计算的和常量成员
      • 反向映射
      • const枚举
  • 7.高级类型
      • 交叉类型(Intersection Types)
      • 联合类型
      • 类型保护
      • Null
      • 类型别名
      • 字符串字面量类型
      • 可辨识联合
      • 索引类型
      • 映射类型
  • 8.模块
    • 介绍
    • 导出
    • 重新导出
    • 导入
      • export = 和 import = require()
    • 生成模块代码
    • 模块解析
  • 9.命名空间
    • 命名空间和模块
  • 10.JSX([脑图](https://www.processon.com/mindmap/5fab4409e0b34d6b5bf6c838))
  • 11.三斜线指令

补充

TS的脑图

VSCode实现用Ctrl+滚轮实现代码的缩放

1、打开"文件->首选项->设置->用户->下面的JSON:
2、点击在settings.json中编辑
3、

 "editor.mouseWheelZoom": true,

即可实现效果!

格式化代码

VS code下 :Shift + Alt + F

打断点

第一步 首先全局ts-node安装

npm install ts-node -g --save  --unsafe-perm=true --allow-root

第二步 在生成tsconfig.json在配置中添加 sourceMap:true。

第三步 在debug中添加一个launch.json文件(Node.js)
前端初探——typescript学习整理_第1张图片

    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Launch TS Program",
            //program的值为ts-node模块的执行程序bin.js的文件路径,XXXX是PC用户名,根据自己的电脑用户名修改
            "program": "C:\\Users\\YY\\AppData\\Roaming\\npm\\node_modules\\ts-node\\dist\\bin.js",
            "sourceMaps": true,
            "outFiles": ["${workspaceFolder}/**/*.js"],
            "args": ["${relativeFile}"],
            "cwd": "${workspaceRoot}",
            "protocol": "inspector"
           
        }
    ]

第四步 成功进入调试模式
前端初探——typescript学习整理_第2张图片

1.基础类型

布尔值

let a: boolean = false;

数字

let b: number = 123; 

字符串

let c: string = "zjw";

数组

let d: number[] = [1,2,3];
let d: Array<number> = [1,2,3];

元组,元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同

let e: [string, number] = ['hello', 10];  //对应位置的元素类型必须一致
e[3] = 'world';  //越界元素可以是联合类型(string | number)

枚举, 使用枚举类型可以为一组数值赋予友好的名字,默认情况下,从0开始为元素编号作为元素枚举值,你可以由枚举值得到元素的名字

enum Color {Red = 1, Green, Blue}
let f: Color = Color.Green;  // 2
let colorName: string = Color[2];  // 'Green'

任何类型

let g: any = 4;
let h: any[] = [1, 'a', true];

没有任何类型, 只能赋值undefiened和null

function a(): void {
	console.log(1,2,3)
}

object表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型

let o: object = {a: 1};

类型断言

// 尖括号形式
let someValue: any = "this is a string"; 
let strLength: number = (<string>someValue).length;

// as语法
//在 tsx 语法(React 的 jsx 语法的 ts 版)中必须用后一种(形如 的语法在 tsx 中表示的是一个 ReactNode,在 ts 中除了表示类型断言之外,也可能是表示一个泛型。)

故在使用类型断言时,统一使用 值 as 类型 这样的语法
let someValue: any = "this is a string"; 
let strLength: number = (someValue as string).length;

2.接口

普通接口

interface myObj {
	label: string;
	color?: string;        //可选属性
	readonly x: number;    //只读属性, 创建后就不能再修改
	[propName: string]: any;    //其他属性
}
/**
 * 只读属性
 * 一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性
 */

function printLabel(Obj: myObj) {
  console.log(Obj.label);
}

let obj = {size: 10, label: "Size 10 Object"};


 //TypeScript具有ReadonlyArray类型,它与Array相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
//用类型断言可以进行重写
a = ro as number[];

readonly vs const

最简单判断该用readonly还是const的方法是看要把它做为变量使用还是做为一个属性。
做为变量使用的话用const,若做为属性则使用readonly

函数类型

除了描述带有属性的普通对象外,接口也可以描述函数类型

interface SearchFunc {
	(source: string, subString: string): boolean;
}
/* 这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。
 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量 */
let mySearch: SearchFunc;

mySearch = function (source: string, subString: string) {
    let result = source.search(subString);
    if (result == -1) {
        return false;
    }
    else {
        return true;
    }
}
// /* 对于函数类型的类型检查来说,函数的参数名不需要与接口里定义的名字相匹配。 
// 比如,我们使用下面的代码重写上面的例子 */
// let mySearch: SearchFunc;
// mySearch = function(src: string, sub: string): boolean {
//   let result = src.search(sub);
//   if (result == -1) {
//     return false;
//   }
//   else {
//     return true;
//   }
// }

可索引的类型

可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。
TypeScript支持两种索引签名:字符串数字
可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。这是因为当使用 number来索引时,JavaScript会将它转换成string然后再去索引对象。

interface SquareConfig {
    color?: string;
    width?: number;
    [propName: string]: any;
}

let squareOptions = { colour: "red", width: 100 };
//#########################################################
// 我们定义了StringArray接口,它具有索引签名。 
// 这个索引签名表示了当用 number去索引StringArray时会得到string类型的返回值。
interface StringArray {
    [index: number]: string;
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

类类型

与C#或Java里接口的基本作用一样,TypeScript也能够用它来明确的强制一个类去符合某种契约

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
}

class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}

对于类的静态部分,例如构造函数,不能直接通过类实施接口进行检查,需要提出处理
//官方文档有详细介绍,接口那一章

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;
}

interface ClockInterface {
    tick();
}

function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("beep beep");
    }
}

let digital = createClock(DigitalClock, 12, 17);

接口可以互相继承,包括多重继承

interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

混合类型

接口可以描述丰富的类型
一个例子就是,一个对象可以同时做为函数和对象使用,并带有额外的属性。

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}

function getCounter(): Counter {
    let counter = <Counter>function (start: number) { };
    counter.interval = 123;
    counter.reset = function () { };
    return counter;
}

let c = getCounter(); //[Function: counter] { interval: 123, reset: [Function (anonymous)] }
c(10);           //[Function: counter] { interval: 123, reset: [Function (anonymous)] }
c.reset();
c.interval = 5.0; //[Function: counter] { interval: 5, reset: [Function (anonymous)] }

接口继承类

当接口继承了一个类类型时,它会继承类的成员但不包括其实现。这意味着当你创建了一个接口继承了一个拥有私有(private)或受保护的成员(protected)的类时,这个接口类型只能被这个类或其子类所实现(implement)。

class Control {
    private state: any;
}

interface SelectableControl extends Control {   //继承了类的接口
    select(): void;
}

class Button extends Control implements SelectableControl {  //正常
    select() { }
}

class Image implements SelectableControl {    // 错误:“Image”类型缺少“state”属性。
    select() { }
}

3.类

公共,私有与受保护的修饰符
public \private\protected
在TypeScript里,类的成员都默认为 public,可以自由访问,当成员被标记成 private时,它就不能在声明它的类的外部访问。

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // 错误: 'name' 是私有的.

protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。若构造函数被标记成 protected。 这意味着这个类不能在包含它的类外被实例化,但是能被继承。

class Person {
    protected name: string;
    constructor(name: string) { this.name = name; }
}

class Employee extends Person {
    private department: string;

    constructor(name: string, department: string) {
        super(name) //子类构造函数必须写supper
        this.department = department;
    }

    public getElevatorPitch() {   //可以在子类的内部访问到保护的属性
        return `Hello, my name is ${this.name} and I work in ${this.department}.`;
    }
}

let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // 错误

readonly修饰符

你可以使用readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。

class Octopus {
    readonly name: string;
    readonly numberOfLegs: number = 8;
    constructor (theName: string) {
        this.name = theName;
    }
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.

//构造函数可以简写 constructor (readonly name: string) {}

存取器

可以在类中设置get/set,控制成员的访问

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";

抽象类
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。抽象类中的抽象方法不包含具体实现并且必须在派生类中实现

abstract class Animal {
    abstract makeSound(): void;   //抽象方法,必须在子类中实现
    move(): void {
        console.log('roaming the earch...');
    }
}

4.函数( 转demo3)

为函数的参数和返回值添加类型

如果函数没有返回任何值,你也必须指定返回值类型为void而不能留空。在TypeScript里我们可以在参数名旁使用?实现可选参数的功能,可选参数必须跟在必须参数后面。

function add(x: number, y?: number): number {
    return x + y;
}

let myAdd = function(x: number, y?: number): number { return x + y; };

重载

可以为同一个函数提供多个函数类型定义来进行函数重载,它查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个。 因此,在定义重载的时候,一定要把最精确的定义放在最前面

function pickCard(x: {suit: string; card: number; }[]): number;  //定义1
function pickCard(x: number): {suit: string; card: number; };  //定义2

// 函数实现
function pickCard(x): any {
    // Check to see if we're working with an object/array
    // if so, they gave us the deck and we'll pick the card
    if (typeof x == "object") {
        let pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
    }
    // Otherwise just let them pick the card
    else if (typeof x == "number") {
        let pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
    }
}

5.泛型

可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: <T>(arg: T) => T = identity;

泛型接口

interface GenericIdentityFn {
    <T>(arg: T): T;
}

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: GenericIdentityFn = identity;

把泛型参数当作整个接口的一个参数

interface GenericIdentityFn<T> {
    (arg: T): T;
}

function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;

泛型类

泛型类使用<>括起泛型类型,跟在类名后面

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

可以定义一个接口interface来描述约束条件,使用这个接口和extends关键字来实现约束

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

在TypeScript使用泛型创建工厂函数时,需要引用构造函数的类类型

function create<T>(c: {new(): T; }): T {
    return new c();
}

6.枚举

数字枚举

定义了一个数字枚举, Up使用初始化为 1。 其余的成员会从 1开始自动增长

enum Direction {
    Up = 1,
    Down,
    Left,
    Right
}

字符串枚举

enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}

计算的和常量成员

每个枚举成员都带有一个值,它可以是常量或计算出来的。规则如下:

1.一个枚举表达式字面量(主要是字符串字面量或数字字面量)
2.一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)
3.带括号的常量枚举表达式
4.一元运算符 +, -, ~其中之一应用在了常量枚举表达式
5.常量枚举表达式做为二元运算符 +, -, *, /, %, <<, >>, >>>, &, |, ^的操作对象。 若常数枚举表达式求值后为 NaN或 Infinity,则会在编译阶段报错。

//常量
enum E1 { X, Y, Z }
enum E2 {
    A = 1, B, C
}
enum FileAccess {
    // constant members
    None,
    Read    = 1 << 1,
    Write   = 1 << 2,
    ReadWrite  = Read | Write,
    // computed member
    G = "123".length
}

反向映射

创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了反向映射,从枚举值到枚举名字

enum Enum {
    A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"

const枚举

常量枚举只能使用常量枚举表达式,不允许包含计算成员

const enum Enum {
    A = 1,
    B = A * 2
}

7.高级类型

d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d \color{#FF0000}{dddddddddddddddddddddddddddddddddddddddddddddd} dddddddddddddddddddddddddddddddddddddddddddddd

交叉类型(Intersection Types)

我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用(在JavaScript里发生这种情况的场合很多!)。用符号&连接不同的类型
例如, Person & Serializable & Loggable同时是PersonSerializableLoggable。 就是说这个类型的对象同时拥有了这三种类型的成员。

function extend<T, U>(first: T, second: U): T & U {
    let result = <T & U>{};    //交叉类型
    for (let id in first) {
        (<any>result)[id] = (<any>first)[id];
    }
    for (let id in second) {
        if (!result.hasOwnProperty(id)) {
            (<any>result)[id] = (<any>second)[id];
        }
    }
    return result;
}

class Person {
    constructor(public name: string) { }
}
interface Loggable {
    log(): void;
}
class ConsoleLogger implements Loggable {
    log() {
        // ...
    }
}
var jim = extend(new Person("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();

联合类型

联合类型用|分隔多个类型,例如string | number,代表可以是string或number类型

function padLeft(value: string, padding: string | number) {
    // ...
}

如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员

interface Bird {
    fly();
    layEggs();
}

interface Fish {
    swim();
    layEggs();
}

function getSmallPet(): Fish | Bird {
    // ...
}

let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim();    // errors

类型保护

类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。 要定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个 类型谓词

function isFish(pet: Fish | Bird): pet is Fish {
    return (<Fish>pet).swim !== undefined;
}

在这个例子里,pet is Fish就是类型断言。 一个断言是 parameterName is Type这种形式,parameterName必须是来自于当前函数签名里的一个参数名。

每当使用一些变量调用isFish时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。

Null

TypeScript具有两种特殊的类型, null和 undefined,默认情况下,类型检查器认为 null与 undefined可以赋值给任何类型。–strictNullChecks标记可以解决此错误:当你声明一个变量时,它不会自动地包含 null或 undefined, 且函数可选参数和对象可选属性也会被自动地加上 | undefined。

let s = "foo";
s = null; // 错误, 'null'不能赋值给'string'

let sn: string | null = "bar";
sn = null; // 可以
sn = undefined; // error, 'undefined'不能赋值给'string | null'
function f(x: number, y?: number) {
    return x + (y || 0);
}
f(1, 2);
f(1);
f(1, undefined);
f(1, null); // error, 'null' is not assignable to 'number | undefined'

class C {
    a: number;
    b?: number;
}
let c = new C();
c.a = 12;
c.a = undefined; // error, 'undefined' is not assignable to 'number'
c.b = 13;
c.b = undefined; // ok
c.b = null; // error, 'null' is not assignable to 'number | undefined'

可以添加!后缀,从变量类型中去除null或 undefined

类型别名

类型别名会给一个类型起个新名字,可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。类型别名还可以用于泛型。其与接口的区别在于不能被 extends和 implements

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;  //联合类型
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    }
    else {
        return n();
    }
}

// T可以是别名,在别名声明的右侧传入
type Container<T> = { value: T };

接口 vs. 类型别名

像我们提到的,类型别名可以像接口一样;然而,仍有一些细微差别。

其一,接口创建了一个新的名字,可以在其它任何地方使用。 类型别名并不创建新名字—比如,错误信息就不会使用别名。 在下面的示例代码里,在编译器中将鼠标悬停在 interfaced上,显示它返回的是Interface,但悬停在aliased上时,显示的却是对象字面量类型。

type Alias = { num: number }
interface Interface {
    num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;

另一个重要区别是类型别名不能被extendsimplements(自己也不能extendsimplements其它类型)。 因为 软件中的对象应该对于扩展是开放的,但是对于修改是封闭的,你应该尽量去使用接口代替类型别名。

另一方面,如果你无法通过接口来描述一个类型并且需要使用联合类型或元组类型,这时通常会使用类型别名。

字符串字面量类型

字符串字面量类型允许你指定字符串必须的固定值,实现类似枚举类型的字符串

type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
    animate(dx: number, dy: number, easing: Easing) {
        if (easing === "ease-in") {
            // ...
        }
        else if (easing === "ease-out") {
        }
        else if (easing === "ease-in-out") {
        }
        else {
            // error! should not pass null or undefined.
        }
    }
}

let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here

可辨识联合

你可以合并单例类型,联合类型,类型保护和类型别名来创建一个叫做可辨识联合的高级模式。个人理解就是联合各个接口,然后这些接口都具有一些共同的属性,这些共同属性称为可辨识的特征或标签

interface Square {
    kind: "square";
    size: number;
}
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(s: Shape) {  // 使用可辨识联合类型
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
    }
}

索引类型

通过索引类型查询keyof T和 索引访问操作符T[K]可以检查属性名是否存在于对象中

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  return names.map(n => o[n]);
}

interface Person {
    name: string;
    age: number;
}
let person: Person = {
    name: 'Jarid',
    age: 35
};

let personProps: keyof Person; // 'name' | 'age'
let strings: string[] = pluck(person, ['name']); // ok, string[]
let strings: string[] = pluck(person, ['age', 'unknown']); // error, 'unknown' is not in 'name' | 'age'

映射类型

TypeScript提供了从旧类型中创建新类型的一种方式,新类型以相同的形式去转换旧类型里每个属性

type Readonly<T> = {
    readonly [P in keyof T]: T[P];   // 每个属性都转化为readonly
}
type Partial<T> = {
    [P in keyof T]?: T[P];     // 每个属性都转化为可选
} 

type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;

TypeScript 2.8在lib.d.ts里增加了一些预定义的有条件类型

Exclude<T, U> --T中剔除可以赋值给U的类型。
Extract<T, U> -- 提取T中可以赋值给U的类型。
NonNullable<T> --T中剔除null和undefined。
ReturnType<T> -- 获取函数返回值类型。
InstanceType<T> -- 获取构造函数类型的实例类型。

type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "a" | "c"
type T04 = NonNullable<string | number | undefined>;  // string | number

function f1(s: string) {
    return { a: 1, b: s };
}

class C {
    x = 0;
    y = 0;
}

type T10 = ReturnType<() => string>;  // string
type T14 = ReturnType<typeof f1>;  // { a: number, b: string }
type T20 = InstanceType<typeof C>;  // C

d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d d \color{#FF0000}{dddddddddddddddddddddddddddddddddddddddddddddd} dddddddddddddddddddddddddddddddddddddddddddddd

8.模块

关于术语的一点说明: 请务必注意一点,TypeScript 1.5里术语名已经发生了变化。
“ 内 部 模 块 ” 现 在 称 做 “ 命 名 空 间 ” 。 “ 外 部 模 块 ” 现 在 则 简 称 为 “ 模 块 ” \color{#FF0000}{“内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”}
这是为了与 ECMAScript 2015里的术语保持一致,(也就是说module X { 相当于现在推荐的写法 namespace X {)。

介绍

从ECMAScript 2015开始,JavaScript引入了模块的概念。TypeScript也沿用这个概念。
模块在其自身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。 相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用 import形式之一。

模块是自声明的;两个模块之间的关系是通过在文件级别上使用imports和exports建立的。
模块使用模块加载器去导入其它的模块。 在运行时,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有依赖。 大家最熟知的JavaScript模块加载器是服务于Node.js的 CommonJS和服务于Web应用的Require.js。

TypeScript与ECMAScript 2015一样,任何包含顶级import或者export的文件都被当成一个模块。

导出

任何声明(比如变量,函数,类,类型别名或接口)都能够通过添加export关键字来导出,还可以通过as关键字对导出的部分重命名

//在Validation.ts下
export interface StringValidator {
    isAcceptable(s: string): boolean;
}

//在ZipCodeValidator.ts下
class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
//###################################
export { ZipCodeValidator };
export { ZipCodeValidator as mainValidator }   //重命名

重新导出

我们经常会去扩展其它模块,并且只导出那个模块的部分内容。 重新导出功能并不会在当前模块导入那个模块或定义一个新的局部变量。

//在ParseIntBasedZipCodeValidator.ts
export class ParseIntBasedZipCodeValidator {
    isAcceptable(s: string) {
        return s.length === 5 && parseInt(s).toString() === s;
    }
}

// 导出原先的验证器但做了重命名
export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator";

//在AllValidators.ts
export * from "./StringValidator"; // exports interface StringValidator

导入

可以使用以下import形式之一来导入其它模块中的导出内容

import { ZipCodeValidator } from "./ZipCodeValidator";
let myValidator = new ZipCodeValidator();

//将整个模块导入到一个变量,并通过它来访问模块的导出部分
import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();

export = 和 import = require()

为了支持CommonJS和AMD的exports, TypeScript提供了export =语法。
export =语法定义一个模块的导出对象。 这里的对象一词指的是类,接口,命名空间,函数或枚举。
若使用export =导出一个模块,则必须使用TypeScript的特定语法import module = require(“module”)来导入此模块。

//ZipCodeValidator.ts
let numberRegexp = /^[0-9]+$/;
class ZipCodeValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export = ZipCodeValidator;

//Test.ts
import zip = require("./ZipCodeValidator");
// Validators to use
let validator = new zip();

生成模块代码

根据编译时指定的模块目标参数–module 模块类型,编译器会生成相应的模块加载系统使用的代码(例如CommonJS、ES6等)

tsc --module commonjs Test.ts

使用其它的JavaScript库
通过将暴露的API定义在.d.ts文件中,来创建外部模块

declare module "url" {
    export interface Url {
        protocol?: string;
        hostname?: string;
        pathname?: string;
    }

    export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url;
}

declare module "path" {
    export function normalize(p: string): string;
    export function join(...paths: any[]): string;
    export let sep: string;
}

在导入时添加/// ,即可直接导入

/// 
import * as URL from "url";
let myUrl = URL.parse("http://www.typescriptlang.org");

模块解析

模块解析是指编译器在查找导入模块内容时所遵循的流程。共有两种可用的模块解析策略:Node和Classic。 你可以使用 --moduleResolution标记来指定使用哪种模块解析策略。若未指定,那么在使用了 --module AMD | System | ES2015时的默认值为Classic,其它情况时则为Node。

9.命名空间

为了不使对象、接口的命名产生冲突,我们可以加入命名空间,并且使用export导出那些需要在命名空间外访问的对象。

namespace Validation {       //命名空间
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }

    const lettersRegexp = /^[A-Za-z]+$/;
    const numberRegexp = /^[0-9]+$/;

    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);
        }
    }
}

// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

同一命名空间可以分割到多个文件,通过加入引用标签来表示关联,最后在编译时使用–outFile标记

//Validation.ts
namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }
}
/// 
//LettersOnlyValidator.ts
namespace Validation {
    const lettersRegexp = /^[A-Za-z]+$/;
    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }
}
// Test.ts
/// 
/// 

// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

tsc --outFile sample.js Test.ts

命名空间和模块

像命名空间一样,模块可以包含代码和声明。 不同的是模块可以声明它的依赖。模块会把依赖添加到模块加载器上,这一点点的花费会带来长久的模块化和可维护性上的便利。模块文件本身已经是一个逻辑分组,不要对模块使用命名空间。

10.JSX(脑图)

JSX是一种嵌入式的类似XML的语法。
它可以被转换成合法的JavaScript,尽管转换的语义是依据不同的实现而定的。
JSX因 React框架而流行,但是也被其它应用所使用。
TypeScript支持内嵌,类型检查以及将JSX直接编译为JavaScript。想要使用JSX必须做两件事:

  • 1.给文件一个.tsx扩展名。
  • 2.启用jsx选项

TypeScript具有三种JSX模式:preservereact和react-native。

你可以通过在命令行里使用--jsx标记或tsconfig.json里的选项来指定模式。

这些模式只在代码生成阶段起作用 - 类型检查并不受影响。 在  preserve模式下生成代码中会保留JSX以供后续的转换操作使用(比如: Babel)。 另外,输出文件会带有 .jsx扩展名。  react模式会生成 React.createElement,在使用前不需要再进行转换操作了,输出文件的扩展名为 .js。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201117194724642.png#pic_center) ## 类型检查 一个JSX表达式可能是环境自带的东西(例如DOM环境里的div或span),也可能是自定义的组件。前者称为固有元素,后者称为基于值的元素

注意:React标识符是写死的硬代码,所以你必须保证React(大写的R)是可用的。 Note: The identifierReact is hard-coded, so you must make React available with an uppercase R.

11.三斜线指令

三斜线指令是包含单个XML标签的单行注释。 注释的内容会做为编译器指令使用。

三斜线指令仅可放在包含它的文件的最顶端。 一个三斜线指令的前面只能出现单行或多行注释,这包括其它的三斜线指令。 如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义。

如果指定了–noResolve编译选项,三斜线引用会被忽略。

///
三斜线指令中最常见的一种。 它用于声明文件间的依赖。三斜线引用告诉编译器在编译过程中要引入的额外的文件。

///
/// 指令相似,这个指令是用来声明依赖的。例如,把/// 引入到声明文件,表明这个文件使用了 @types/node/index.d.ts里面声明的名字; 并且,这个包需要在编译阶段与声明文件一起被包含进来

///
这个指令把一个文件标记成默认库。 你会在 lib.d.ts文件和它不同的变体的顶端看到这个注释,这个指令告诉编译器在编译过程中不要包含这个默认库。

你可能感兴趣的:(学习笔记,前端,typescript,编译器,reactjs,javascript)