一.使用ts的vue项目,多了几个文件

1.tsconfig.json: typescript配置文件,主要用于指定待编译的文件和定义编译选项
2.shims-tsx.d.ts: 允许.tsx 结尾的文件,可在 Vue 项目中编写 jsx 代码
3.shims-vue.d.ts: 主要用于 TypeScript 识别.vue 文件,Ts 默认并不支持导入 vue 文件

结合vue-property-decorator使用ts

二.在 vue 中使用 typescript 非常好用的几个库

  1. vue-class-component: 类的装饰器
    2.vue-property-decorator: 基于 vue 组织里 vue-class-component 所做的拓展import { Vue, Component, Inject, Provide, Prop, Model, Watch, Emit, Mixins } from ‘vue-property-decorator’
    安装

npm i -S vue-property-decorator

// tsconfig.json
{
  "compilerOptions": {
    // 与 Vue 的浏览器支持保持一致
    "target": "es5",
    // 这可以对 `this` 上的数据 property 进行更严格的推断
    "strict": true,
    // 如果使用 webpack 2+ 或 rollup,可以利用 tree-shake:
    "module": "es2015",
    "moduleResolution": "node"
  }
}

注意你需要引入 strict: true (或者至少 noImplicitThis: true,这是 strict 模式的一部分) 以利用组件方法中 this 的类型检查,否则它会始终被看作 any 类型。
3.vuex-module-decorators: 用 typescript 写 vuex 很好用的一个库import { Module, VuexModule, Mutation, Action, MutationAction, getModule } from ‘vuex-module-decorators’

三.vue3.0+typescript搭建脚手架

1.输入命令

vue create vue3.0-ts-demo

2.选择安装的配置

在这里插入图片描述
1.选择3.x
在这里插入图片描述
2.上面如果要求安装typescript,就会多出下面的一步。就会问你:是否选择class风格的装饰器
在这里插入图片描述
class声明一个组件的名称,然后继承vue的这个写法。这个写法官方是弃用这种写法的,所以我们一般不用。或者选择N.
在这里插入图片描述
3.询问我们:是否使用babel与typescript一起用于自动检测的填充。也就是自动生成的一些东西。选择Y
在这里插入图片描述
4.是否单独存储
在这里插入图片描述
完整:
在这里插入图片描述
5.安装ant-design

npm install ant-design-vue@next
在这里插入图片描述
引入到main.ts里面

6.根目录添加一个vue.config.js
在这里插入图片描述

四.typescript语法

1.基础类型

1)布尔值:

let isDone: boolean = false;

2)数字:

let decLiteral: number = 6;

3)字符串:

let name: string = `Gene`;
//还可以使用模板字符串
let sentence: string = `Hello, my name is ${ name }.`

4)数组:

两种方式可以定义数组。
第一种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组:

let list: number[] = [1, 2, 3];

第二种方式是使用数组泛型,Array<元素类型>:

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

5)元组 Tuple:

我们知道数组中元素的数据类型都一般是相同的(any[] 类型的数组可以不同),如果存储的元素数据类型不同,则需要使用元组。
元组中允许存储不同类型的元素,元组可以作为参数传递给函数。

元组的使用情况比较少

const xiaoJieJie : [string,string,number]=['a','b',12]
const xiaoJieJies :[string,string,number][]=[
    ['a','b',12],
    ['a','b',12],
    ['a','b',12],
]

6)Any:

当你只知道一部分数据的类型时,any类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:

let list: any[] = [1, true, "free"];
list[1] = 100;
<script lang="ts">
import { defineComponent,reactive} from "vue";

export default defineComponent({
  name: "Home",
  setup(props) {
    const form = reactive<any>({
      layout:{
      label:'姓名',
      value:'name'
    }
    })
    return{
      abc
    }
  }
});
</script>

7)Void:

某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void:

function warnUser(): void {
    console.log("This is my warning message");
}

声明一个void类型的变量没有什么大用,因为你只能为它赋予undefined和null:

let unusable: void = undefined;

8)Null 和 Undefined:

TypeScript里,undefined和null两者各自有自己的类型分别叫做undefined和null。 和 void相似,它们的本身的类型用处不是很大:

// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;

9)Never:

never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型;

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
    return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
    }
}

10)Object:

declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK
<script lang="ts">
import { defineComponent,onMounted,ref} from "vue";

export default defineComponent({
  name: "Home",
  setup(props) {
    const abc=ref<string>('123')
    onMounted(() => {
      console.log(abc.value)
    })
    return{
      abc
    }
  }
});
</script>

11)enum 枚举

使用场景:接口给前端返回一个status字段
在这里插入图片描述

2.变量声明

可以用var,let,const,和js使用的方法一样

3.接口

TS新增了一个重要概念:接口,分为对象类型接口和函数类型接口
接口可以约束对象,函数,类的结构和类型,是一种代码协作必须遵守的契约

interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

1)可选属性:

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号。

interface SquareConfig {
  color?: string;
  width?: number;
}

2)只读属性

  1. 一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly来指定只读属性:。
    下面是通过赋值一个对象字面量来构造一个Point。 赋值后, x和y再也不能被改变了。
interface Point {
    readonly x: number;
    readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
  1. ReadonlyArray类型,可以确保数组创建后再也不能被修改
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
  1. 把整个ReadonlyArray赋值到一个普通数组也是不可以的,用类型断言重写:
a = ro as number[];
  1. 判断该用readonly还是const的方法
    看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用readonly。

3)额外的属性检查

比如说多添加一些其他属性,下面的写法就不行

interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
    // ...
}

// error: 'colour' not expected in type 'SquareConfig'
let mySquare = createSquare({ colour: "red", width: 100 });

要解决,最简便的方法是使用类型断言:

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

最后一种跳过这些检查的方式,是将这个对象赋值给一个另一个变量: 因为 squareOptions不会经过额外属性检查,所以编译器不会报错。

let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);

4)函数类型

除了描述带有属性的普通对象外,接口也可以描述函数类型。
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。

interface SearchFunc {
	//冒号前面是参数列表,后面是返回值类型
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
  let result = source.search(subString);
  return result > -1;
}

5)可索引的类型

TypeScript支持两种索引签名:字符串和数字。 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。

interface StringArray {
  [index: number]: string;
}

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

let myStr: string = myArray[0];

6)类类型

实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。

举例来说,门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它:

TypeScript也能够用它来明确的强制一个类去符合某种契约。

interface Alarm {
    alert(): void;
}

class Door {
}

class SecurityDoor extends Door implements Alarm {
    alert() {
        console.log('SecurityDoor alert');
    }
}

class Car implements Alarm {
    alert() {
        console.log('Car alert');
    }
}

一个类可以实现多个接口:

interface Alarm {
    alert(): void;
}

interface Light {
    lightOn(): void;
    lightOff(): void;
}

class Car implements Alarm, Light {
    alert() {
        console.log('Car alert');
    }
    lightOn() {
        console.log('Car light on');
    }
    lightOff() {
        console.log('Car light off');
    }
}

上例中,Car 实现了 Alarm 和 Light 接口,既能报警,也能开关车灯。

4.类

1)基本使用

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

let greeter = new Greeter("world");

2)继承extends

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);
dog.bark();

3)公共,私有与受保护的修饰符

public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

3-1 默认为 public

自由的访问程序里定义的成员

class Animal {
    public name: string;
    public constructor(theName: string) { this.name = theName; }
    public move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}
3-2 理解 private

当成员被标记成 private时,它就不能在声明它的类的外部访问

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

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

如果其中一个类型里包含一个 private成员,那么只有当另外一个类型中也存在这样一个 private成员, 并且它们都是来自同一处声明时,我们才认为这两个类型是兼容的。 对于 protected成员也使用这个规则。

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

class Rhino extends Animal {
    constructor() { super("Rhino"); }
}

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

let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");

animal = rhino;
animal = employee; // 错误: Animal 与 Employee 不兼容.

因为 Animal和 Rhino共享了来自 Animal里的私有成员定义 private name: string,因此它们是兼容的。 尽管 Employee里也有一个私有成员 name,但它明显不是 Animal里面定义的那个。

3-3 理解 protected

protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。如下,Employee是由 Person派生而来的。

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)
        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); // 错误

4)存取器getters/setters

TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。

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) {
    alert(employee.fullName);
}

5)静态属性static

创建类的静态成员,这些属性存在于类本身上面而不是类的实例上。

class Grid {
    static origin = {x: 0, y: 0};
    calculateDistanceFromOrigin(point: {x: number; y: number;}) {
        let xDist = (point.x - Grid.origin.x);
        let yDist = (point.y - Grid.origin.y);
        return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
    constructor (public scale: number) { }
}

let grid1 = new Grid(1.0);  // 1x scale
let grid2 = new Grid(5.0);  // 5x scale

console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

6)抽象类abstract

抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。

abstract class Department {

    constructor(public name: string) {
    }

    printName(): void {
        console.log('Department name: ' + this.name);
    }

    abstract printMeeting(): void; // 必须在派生类中实现
}

let department: Department; // 允许创建一个对抽象类型的引用
department = new Department(); // 错误: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
department.generateReports(); // 错误: 方法在声明的抽象类中不存在

5.函数

1)函数类型

  • 为函数定义类型
function add(x: number, y: number): number {
    return x + y;
}

let myAdd = function(x: number, y: number): number { return x + y; };
  • 书写完整函数类型
    返回值类型是函数类型的必要部分,如果函数没有返回任何值,你也必须指定返回值类型为 void而不能留空。
let myAdd: (x: number, y: number) => number =
    function(x: number, y: number): number { return x + y; };

2)可选参数

  • 默认情况下,TypeScript里的每个函数参数都是必须的,也就是说传递给一个函数的参数个数必须与函数期望的参数个数一致。
function buildName(firstName: string, lastName: string) {
    return firstName + " " + lastName;
}

let result1 = buildName("Bob");                  // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result3 = buildName("Bob", "Adams");         // ah, just right
  • 可选参数需要在参数名旁使用 ?实现,可选参数必须跟在必须参数后面
function buildName(firstName: string, lastName?: string) {

}

3)默认值

function buildName(firstName: string, lastName = "Smith") {
    return firstName + " " + lastName;
}

与普通可选参数不同的是,带默认值的参数不需要放在必须参数的后面。 如果带默认值的参数出现在必须参数前面,用户必须明确的传入 undefined值来获得默认值。

function buildName(firstName = "Will", lastName: string) {
    return firstName + " " + lastName;
}

let result1 = buildName("Bob");                  // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result3 = buildName("Bob", "Adams");         // okay and returns "Bob Adams"
let result4 = buildName(undefined, "Adams");     // okay and returns "Will Adams"

4) 剩余参数

必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在JavaScript里,你可以使用 arguments来访问所有传入的参数。

function buildName(firstName: string, ...restOfName: string[]) {
  return firstName + " " + restOfName.join(" ");
}

let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");

5)this

跟之前的javascripty语法差不多

6.泛型

泛型:软件工程中,我们不仅要创造定义良好的API,同时也要考虑可重用行,组件不仅能够支持当前的数据类型,同时也能够支持未来数据类型。
通俗理解:泛型就是解决类、接口、方法的复用性以及对不特定类型的数据的支持
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

function getDate<T>(value:T):T {
  return value;
}
getDate<number>(123);
getDate<string>('123');

1)基本使用

需求:这个函数会返回任何传入它的值,也就是返回类型和传入参数的类型一致。

可以用两种方法使用泛型。
第一种是,传入所有的参数,包含类型参数:

let output = identity<string>("myString");  // type of output will be 'string'

第二种方法更普遍。利用了类型推论 – 即编译器会根据传入的参数自动地帮助我们确定T的类型:

let output = identity("myString");  // type of output will be 'string'

注意我们没必要使用尖括号(<>)来明确地传入类型;编译器可以查看myString的值,然后把T设置为它的类型。

2)使用泛型变量

如果我们想同时打印出arg的长度,可以通过下面的两种方法实现:

function loggingIdentity<T>(arg: T[]): T[] {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}
//或者
function loggingIdentity<T>(arg: Array<T>): Array<T> {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}

如果不指定是数组的话,会报错,因为使用了arg的.length属性。而这个函数的人可能传入的是个数字,而数字是没有 .length属性的。

3)泛型类型

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

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

let myIdentity: GenericIdentityFn = identity;

4)泛型类

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

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

5)泛型约束

创建一个包含 .length属性的接口,使用这个接口和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;
}

现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:

loggingIdentity(3);  // Error, number doesn't have a .length property
  • 在泛型约束中使用类型参数
    你可以声明一个类型参数,且它被另一个类型参数所约束。 比如,现在我们想要用属性名从对象里获取这个属性。 并且我们想要确保这个属性存在于对象 obj上,因此我们需要在这两个类型之间使用约束。
function getProperty(obj: T, key: K) {
    return obj[key];
}

let x = { a: 1, b: 2, c: 3, d: 4 };

getProperty(x, "a"); // okay
getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.
  • 在泛型里使用类类型
function create<T>(c: {new(): T; }): T {
    return new c();
}

7.枚举

1)数字枚举

如下:Up的值为 0, Down的值为 1等等。默认是自增长的,注意每个枚举成员的值都是不同的。

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

设置默认值,就是 Up使用初始化为 1。 其余的成员会从 1开始自动增长。 换句话说, Direction.Up的值为 1, Down为 2, Left为 3, Right为 4。

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

不带初始化器的枚举或者被放在第一的位置,或者被放在使用了数字常量或其它常量初始化了的枚举后面。这些都是不被允许的。

2)字符串枚举

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

3)计算的和常量成员

每个枚举成员都带有一个值,它可以是 常量或 计算出来的。 当满足如下条件时,枚举成员被当作是常量:

  • 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值 0:
// E.X is constant:
enum E { X }
  • 它不带有初始化器且它之前的枚举成员是一个 数字常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加1。
// All enum members in 'E1' and 'E2' are constant.
enum E1 { X, Y, Z }
enum E2 {
    A = 1, B, C
}

8.命名空间

1)使用命名空间的验证器

这些接口和类在命名空间之外也是可访问的,所以需要使用 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);
        }
    }
}

// Some samples to try
let strings = ["Hello", "98052", "101"];

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

// Show whether each string passed each validator
for (let s of strings) {
    for (let name in validators) {
        console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);
    }
}

2)分离到多文件

当应用变得越来越大时,我们需要将代码分离到不同的文件中以便于维护。

  • 多文件中的命名空间
    ts文件的额写法和上面一样,然后通过我们可以编译每一个文件(默认方式),那么每个源文件都会对应生成一个JavaScript文件。 然后,在页面上通过

3)使用其它的JavaScript库

  • 外部命名空间
    因为这个库通过一个
declare namespace D3 {
    export interface Selectors {
        select: {
            (selector: string): Selection;
            (element: EventTarget): Selection;
        };
    }

    export interface Event {
        x: number;
        y: number;
    }

    export interface Base extends Selectors {
        event: Event;
    }
}

declare var d3: D3.Base;

9.模块

请务必注意一点,TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”

模块是自声明的;两个模块之间的关系是通过在文件级别上使用imports和exports建立的。

1)导出

  1. 导出声明
    任何声明(比如变量,函数,类,类型别名或接口)都能够通过添加export关键字来导出。
export interface StringValidator {
    isAcceptable(s: string): boolean;
}
  1. 导出语句
    对导出的部分重命名
class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export { ZipCodeValidator as mainValidator };
  1. 重新导出
    ParseIntBasedZipCodeValidator.ts
export class ParseIntBasedZipCodeValidator {
    isAcceptable(s: string) {
        return s.length === 5 && parseInt(s).toString() === s;
    }
}

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

或者一个模块可以包裹多个模块,并把他们导出的内容联合在一起通过语法:export * from “module”。
AllValidators.ts

export * from "./StringValidator"; // exports interface StringValidator
export * from "./LettersOnlyValidator"; // exports class LettersOnlyValidator
export * from "./ZipCodeValidator";  // exports class ZipCodeValidator
  1. 默认导出
    每个模块都可以有一个default导出。 默认导出使用 default关键字标记;并且一个模块只能够有一个default导出。 需要使用一种特殊的导入形式来导入 default导出。
export default "123";

2)导入

import形式之一来导入其它模块中的导出内容

  1. 导入一个模块中的某个导出内容
import { ZipCodeValidator } from "./ZipCodeValidator";
let myValidator = new ZipCodeValidator();
//对导入内容重命名
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
let myValidator = new ZCV();
  1. 将整个模块导入到一个变量,并通过它来访问模块的导出部分
import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();

3)export = 和 import = require()

export default 语法并不能兼容CommonJS和AMD的exports。虽然CommonJS和AMD的环境里都有一个exports变量,这个变量包含了一个模块的所有导出内容。

为了支持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");

// Some samples to try
let strings = ["Hello", "98052", "101"];

// Validators to use
let validator = new zip();

// Show whether each string passed each validator
strings.forEach(s => {
  console.log(`"${ s }" - ${ validator.isAcceptable(s) ? "matches" : "does not match" }`);
});

10.装饰器

例如,有一个@sealed装饰器,我们会这样定义sealed函数:

function sealed(target) {
    // do something with "target" ...
}

11.高级类型

1)交叉类型

交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。

例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。 就是说这个类型的对象同时拥有了这三种类型的成员

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

2)联合类型

联合类型表示一个值可以是几种类型之一。 我们用竖线( |)分隔每个类型,所以 number | string | boolean表示一个值可以是 number, string,或 boolean。如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。

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

let indentedString = padLeft("Hello world", true); // errors during compilation

当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:

function getLength(something: string | number): number {
    return something.length;
}

上例中,length 不是 string 和 number 的共有属性,所以会报错。访问 string 和 number 的共有属性是没问题的:

function getString(something: string | number): string {
    return something.toString();
}

12.类型断言

1)语法

as 类型

或者

<类型>

2)类型断言的用途

  • 联合类型可以被断言为其中一个类型
  • 父类可以被断言为子类
  • 任何类型都可以被断言为 any
  • any 可以被断言为任何类型

13.元组

数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。

1)简单的例子

定义一对值分别为 string 和 number 的元组:

let tom: [string, number] = ['Tom', 25];

当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项。

let tom: [string, number];
tom = ['Tom', 25];

可以赋值或访问一个已知索引的元素时,也会得到正确的类型。但是当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项。

像下面这样就会报错:

let tom: [string, number];
tom = ['Tom'];

// Property '1' is missing in type '[string]' but required in type '[string, number]'.

2)越界的元素

当添加越界的元素时,它的类型会被限制为元组中每个类型的联合类型:

let tom: [string, number];
tom = ['Tom', 25];
tom.push('male');
tom.push(true);//这个类型不可以
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐