TyprScript

TyprScript基础知识

一、TyprScript安装与编译

TyprScript介绍

TypeScript是JavaScript类型的超集,它可以编译成纯JavaScript。TypeScript可以在任何浏览器、任何计算机和任何操作系统上运行,并且是开源的。

TyprScript官网open in new window

TyprScript

TypeScript可以编译出纯净、 简洁的JavaScript代码,并且可以运行在任何浏览器上、Node.js环境中和任何支持ECMAScript 3(或更高版本)的JavaScript引擎中。

安装环境

npm i typescript -g //全局安装typescript

npm init -y //进入文件夹,初始化项目,生成package.json文件

tsc --init //创建tsconfig.json文件

npm i @types/node -S //这个主要是解决模块的声明文件问题

构建你的第一个TypeScript文件

在编辑器,将下面的代码输入到greeter.ts文件里:

function greeter(person) {
    return "Hello, " + person;
}

let user = "Jane User";

document.body.innerHTML = greeter(user);

编译代码

在命令行上,运行TypeScript编译器

tsc greeter.ts

二、原始数据类型

js中数据类型分两种,原始数据类型和对象类型,原始类型包括:布尔值、数字、字符串、null、undefined以及Symbol。

布尔值

在TypeScript中,使用boolean定义布尔值类型

let isStatus:boolean=true;

数字

使用number定义数值类型

let isNumber:number=1;

//16进制(编译后显示10进制数字)
let isNumber16:number:=0b1010;

字符串

使用string定义字符串类型:

let isString:string='hello word';

let year:number=2020;

let add:string=`${isString},${year}!`;

空值

JavaScript 没有空值(Void)的概念,在TypeScript中,可以用void表示没有任何返回值的函数.

function alertName():void{
    alert("my name is tom")
}

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

let unusable: void = undefined;

Null 和 Undefined

在TypeScript中可以使用null和undefined来定义这两个原始数据类型

let u:undefined=undefined;

let n:null=null;

与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说undefined类型的变量,可以赋值给number类型的变量,而void类型的变量不能赋值给number类型的变量:

let num:number=undefined;//不会报错

let u:undefined;

let num: number = u;//也不会报错

let u:void;

let num:number=u;// Type 'void' is not assignable to type 'number'.

任意值

任意值(Any)用来表示允许赋值为任意类型。

什么是任意值类型

如果是一个普通类型,在赋值过程中改变类型是不被允许的:

let renyiString:string='string';

renyiString=8;

//Type 'number' is not assignable to type 'string'.

但如果类型是Any,类型,则允许被赋值为任意类型:

let renyiString:any='string';

renyiString=8;

在任意值上访问任何属性都是允许的:

let anyThis:any='hello';

console.log(anyThis.myName)

console.log(anyThis.myName.firstName)

也允许调用任何方法:

let anyThing: any = 'Tom';

anyThing.setName('Jerry');

anyThing.setName('Jerry').sayHello();

anyThing.myName.setFirstName('Cat');

可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。

未声明类型的变量

变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型:

let something;

something = 'seven';

something = 7;

something.setName('Tom');

等于

let something: any;

something = 'seven';

something = 7;

something.setName('Tom');

三、数组的类型

基础表示

「类型 + 方括号」表示法

let numbers:number[]=[1,2,3,4,5]

此时不允许出现其他类型,而且如果使用数组中push等方法,添加元素也得符合相应类型。

数组泛型

我们也可以使用数组泛型(Array Generic) Array 来表示数组

let fibonacci:Array<number>=[1,2,3,4]

如果数组中又有number类型又有string类型,则可以用|符号来区别定义:

let arr:(number|string)[]=['tom',1];

数组中对象类型的定义

项目中经常遇到数组中有对象的存在,对于这种就比较麻烦了,比如:

let arr:{name:string,age:number}[]=[
    {name:'tom',age:18}
]

这样就比较麻烦了,我们可以使用ts中的类型别名来解决这个问题:

type PeopleType={name:String,age:Number};

let arr:PeopleType[]=[
    {name:'bob',age:19}
]

也可以用类型定义也可以解决:

class PeopleType{
    name:string;
    age:number
}

let arr:PeopleType[]=[
    {name:'bob',age:19}
]

数组的使用和类型约束

在数组中如果里面又有string和number,可以使用|来进行定义,但一定程度上并不严格。比如改成下面这种格式:

let arr:(number|string)[]=[111,'222',111];

ts并没有报错,如果想要严格限制,则可以这样进行约束:

let arr:[number,string,number]=[111,'222',111];

四、接口和类

Interface接口

比如我们要做一个筛选,吧不符合条件的过滤出去,我们可能会这样写:

const types=(name:string,age:number,height:number)=>{
    age>=20 && height>=180 && console.log('符合条件');

    age<=20 && height <180 &&console.log('不符合')
};

types('tom',20,180) //符合条件

但如果又修改了一些需求,可能还会去大量变更代码,在开发中,代码能复用肯定是最好的,所以可以把一些重复的代码抽离出来:

interface People{
    name:string;
    age:number;
    height:number;
}

const types=(people:People)=>{
    people.age>=20 && people.height>=180 && console.log('符合条件');

    people.age<=20 && people.height <180 &&console.log('不符合')
};

const choose=(people:People)=>{
    console.log(people.name+'----'+people.age+'---'+people.height)
}

const people={
    name:'tom',
    age:18,
    height:178
}

types(people);

choose(people);

接口与类型别名的区别

看起来两者没有什么区别,但有个小细节:类型别名可以直接给类型,接口必须代表对象

type People=string;

interface People{
    name:string;
    age:number;
    height:number;
}

如果传入的参数中有不确定项,我们可以使用 ?: 来进行处理:

interface People{
    name:string;
    age:number;
    height:number;
    say?:string
};

const types=(people:People)=>{
    people.age>=20 && people.height>=180 && console.log('符合条件');

    people.age<=20 && people.height <180 &&console.log('不符合')
};

const people={
    name:'tom',
    age:18,
    height:178
}
const people2={
    name:'tom',
    age:18,
    height:178,
    say:'hello'
}

types(people);

types(people2);

这时候又有新需求了,如何在后面加入任意多的字段?这时候我们就可以这么写:

interface People{
    name:string;
    age:number;
    height:number;
    say?:string;
    [propname:string]:any;
}

const types=(people:People)=>{
    people.age>=20 && people.height>=180 && console.log('符合条件');

    people.age<=20 && people.height lt;180 &&console.log('不符合')
};

const people={
    name:'tom',
    age:18,
    height:178,
    say:'hello',
    add:'new add',
    addNumber:123
}

types(people);

接口里的方法

接口不仅仅可以存属性,也可以存方法:

interface People{
    name:string;
    age:number;
    height:number;
    goto():string;
}

const types=(people:People)=>{
    people.age>=20 && people.height>=180 && console.log('符合条件');

    people.age<=20 && people.height <180 &&console.log('不符合')
};

const people={
    name:'tom',
    age:18,
    height:178,
    goto(){
        return 'hello'
    }
}

types(people)

接口和类的约束

在ES6中是有类的概念,类可以和接口相结合:

interfacePeople{
    name:string;
    age:number;
    height:number;
    goto():string;
}
const types=(people:People)=>{
    people.age>=20 && people.height>=180 && console.log('符合条件');

    people.age<=20 && people.height <180 &&console.log('不符合')
};

const people={
    name:'tom',
    age:18,
    height:178,
    say:'hello',
    add:'new add',
    addNumber:123,
    goto(){
        return 'hello'
    }
};

class newPeople implementsPeople{
    name='bob';
    age=19;
    height:190;
    say:'hello';
    add:'new add2';
    addNumber:123;
    goto(){
        return 'hi'
    }
};

let a=new newPeople();
console.log(a.goto())
types(people)

接口的继承

接口与接口也是可以继承的:

interfacePeople{
    name:string;
    age:number;
    height:number;
    say?:string;
    [propname:string]:any;
    goto():string;
}

const types=(people:newPeople)=>{
    people.age>=20 && people.height>=180 && console.log('符合条件');

    people.age<=20 && people.height <180 &&console.log('不符合')
};

const people={
    name:'tom',
    age:18,
    height:178,
    say:'hello',
    add:'new add',
    addNumber:123,
    goto(){
        return 'hello'
    },
    back(){
        console.log(1)
    }
}

interface newPeople extendsPeople{
    back():void
}
types(people)

五、类的基本使用

首先,我们先创建一个类:

class Test{
    name='say'
    say(){
        return this.name
    }
};

这就是平时所写的类,在ts中的继承和ES6的继承是一样的,关键字也是extends,比如我们这里新建个类,继承Text;

classTest{
    name='say'
    say(){
        return this.name
    }
};
class NewTest extendsTest{
    back(){
        return 'hello'
    }
}

const tom=new NewTest();

console.log(tom.say());
console.log(tom.back());

super关键字的使用

如果想在say方法中后面加点东西,可以这么操作:

class Test{ name='say' say(){ return this.name } }; class NewTest extends Test{ back(){ return 'hello' } say(){ return super.say()+'----hello' } } const tom=new NewTest(); console.log(tom.say()); console.log(tom.back());

ts中类的访问类型

ts中的访问类型就是基于三个关键字:private、protected以及pubilc这三种访问类型。看例子,先定义一个类,然后用这个类的对象,进行赋值:

class Person{
    name:string
}

const person=new Person();

person.name='tom';

console.log(person.name)

pubilc

运行可以看到正常的输出内容,这是因为如果不对name的访问属性进行定义,那么他的默认属性就是pubilc,从字面意思来看,它的意思就是公用的,允许在类的内部和外部被调用。

classPerson{
    public name:string
}

const person=new Person();

person.name='tom';

console.log(person.name)

private

private的属性意思就是只允许在类的内部调用,不能在外部调用

class Person{
    private name:string;
    public say(){
        console.log(this.name+'-----hello');
    }
}

const person=new Person();

person.name='tom';//报错

console.log(person.name)//报错

protected

protected允许在类内以及继承的子类中使用,把刚刚的name改成protected属性,这时候在外部就会报错,这时候再写一个继承,代码如下:

classPerson{
    protected name:string;
    public say(){
        console.log(this.name+'-----hello');
    }

}

const person=new Person();

person.name='tom';//报错

console.log(person.name)//报错

class NewPerson extendsPerson{
    public back(){
        console.log(this.name)
    }
}

const newperson=new NewPerson();

newperson.back()//正常

类的构造函数

首先新建一个类:Person,定义一个name,并在new的时候进行参数传递,然后打印出来,这时候我们就可以使用构造函数constructor :

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

const person=new Person('tom');
console.log(person.name);

可以看到可以打印出来,但是上面写法有点麻烦,还可以再进行简化:

classPerson{
    constructor(pubilc name:string){}
};
const person=new Person('tom');
console.log(person.name);

类继承中的构造器写法

普通的书写方法上面已经演示了,在子类中使用构造函数需要用super()调用父类的构造函数,直接看代码:

classPerson{
    constructor(pubilc name:string){}
};

class Teacher extendsPerson{
    constructor(pubilc age:number){
        super()
    }
};

const teacher=new Teacher(18);
console.log(teacher.age+'---'+teacher.name);

ts中类的Getter、Setter、static以及readonly

在上面中提到了访问类型private,它的最大用处就是封装一个书写,然后通过Getter和Setter去访问和修改:

classPerson{
    constructor(private _age:number){

    }
}

如果想让别人知道,就可以使用Getter来实现,他并不是一个方法,只是一个属性:

classPerson{
    constructor(private _age:number){

    };
    get age(){
        return this._age
    }
};

const person=new Person(30);
console.log(person.age)

这时候你可能会觉得这不是多此一举吗?但在Getter中可以对 _age进行处理:

classPerson{
    constructor(private _age:number){

    };
    get age(){
        return this._age-10
    }
};

const person=new Person(30);
console.log(person.age)

既然 _age是私有的,我们无法进行改变,这时候就可以用Setter进行改变:

classPerson{
    constructor(private _age:number){

    };
    get age(){
        return this._age
    }
    set age(age:number){
        this._age=age
    }
};

const person=new Person(30);

person.age=20;

console.log(person.age)

在类中,如果想用这个类的实例,就必须先进行new操作,但有没有一种方法不需要new就可以?

//常规方法
classPerson{
    say(){
        return 'say hello'
    }
};

const person=new Person();
console.log(person.say());

在ts中,我们不想new的话可以这么写:

class Person{
    static say(){
        return 'say hello'
    }
};

console.log(Person.say())

readonly在初始化后赋值,以后就不能进行修改

class Person{
    constructor(readonly name:string){}
}
let p=new Person('tom');

p.name='bob'//报错

console.log(p.name);

类的抽象类

abstract 用于定义抽象类和其中的抽象方法。它有几个特点:

抽象类是不允许实例化的

//错误演示
abstract class Person {
    public abstract say()
}
const tom=new Person()//无法创建抽象类的实例

抽象类中的抽象方法必须被子类实现:

//错误演示
abstract classPerson {
    abstract say(){
        console.log('hello')
    }
}
class Tom extendsPerson{
    say(){
        console.log('say hello')
    }
};
//正确方法
abstract classPerson {
    abstract say()
}
class Tom extendsPerson{
    say(){
        console.log('say hello')
    }
};

tsconfig.json配置

生成

我们可以通过tsc --init去生成ts配置文件:

tsc --init //终端执行

编译

会生成一个tsconfig.json文件,我们在ts文件中可以随便写点什么:

//测试tsconfig.json
const test:string='tsconfig.json';

然后打开tsconfig.json文件,找到complilerOptions属性下的removeComments:true(这个配置是编译后不输出注释),取消掉注释,然后执行命令:

tsc

这时候打开生成的js文件,发现没有注释,说明成功。

include 、exclude 和 files

如果有多个ts文件,只想编译一个可以在ts配置项中加入include:

"include":["text.ts"],

如果想除了某个文件不编译,剩下的都编译,可以使用exclude:

"exclude":["text.ts"],

files和include没有什么区别:

"files":["text.ts"],

compilerOptions配置

removeComments

这个配置意思就是编译后不输出注释

strict

这个设置为true,就代表严格执行ts语法,要严格按照ts语法来编写。

noImplicitAny

允许你的注解类型 any 不用特意表明,如果此时设置了true,看例子:

//这时候编译会报错
function test(name) {
    return name;
}

//正确
function test(name:any) {
    return name;
}
strictNullChecks

意思就是,不强制检查null类型,此时如果配置为true看例子:

//此时就不会报错
const test: string = null;
outDir和rootDir

此项配置是来指定文件目录和打包后存放目录,rootDir为文件目录,outDir为打包后保存的目录

{
    "outDir": "./build" ,
    "rootDir": "./src" ,
}

编译ES6语法

可以使用target和allowJs,target默认为true

"target":'es5' ,  // 这一项默认是开启的,你必须要保证它的开启,才能转换成功
"allowJs":true,   // 这个配置项的意思是联通

sourceMap

sourceMap 简单说,Source map 就是一个信息文件,里面储存着位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置。有了它,出错的时候,除错工具将直接显示原始代码,而不是转换后的代码。这无疑给开发者带来了很大方便。

noUnusedLocals

设置noUnusedLocals为true,编译代码:

const name:tring='111';
export const age = "text";

这时候就会报错,因为有name变量没有使用。

六、联合类型和类型保护

联合类型

联合类型的意思就是允许一个类型有两种或者两种以上的类型:

interfaceTest{
    name:string;
    say:()=>{

    }
}

interfaceTest2{
    name:string;
    call:()=>{};
}

function Tom(fun: Test | Test2){
    console.log(fun.name)
}

如果此时修改一下方法:

function Tom(fun: Test | Test2){
    fun.say()
}
//报错

这是因为只能访问两个类型的共有方法。

类型保护-类型断言

上面的方法,如果修改完报错,这时候我们可以用as来判断:

interfaceTest {
    text: boolean;
    say(): void
}

interfaceTest2 {
    text: boolean;
    skill():string
}
function judgeWho(val: Waiter | Teacher) {
    if (val.text) {
    (val as Teacher).skill();
    }else{
    (val as Waiter).say();
    }
}

const a={
    text:true,
    skill:function():void{
        console.log(1)
    },
    say:function():void{
        console.log(2)
    }
}
judgeWho(a) //1

类型保护-in语法

in方法与断言比较类似,使用方法如下:

interfacePerson{
    name:string;
    say:()=>{

    }
}

interfacePerson2{
    name:string;
    todo:()=>{

    }
};

function tom(val:Person|Person2){
    if('todo' in val){
        val.todo()
    }else{
        val.say()
    }
}

类型保护-typeof语法

可以用typeof方法来判断:

function add(name:string|number,name2:string|number){
    if(typeof name==='string'||typeof name2=='string'){
        return name+'---'+name2
    }else{
        return name+name2
    }
}

add(1,2)

类型保护-instanecof语法

如果要保护类型是一个对象,就可以使用instanceof:

classNumObject{
    num:number
}

function numbers(num1:object|NumObject,num2:object|NumObject){
    if(num1 instanceof NumObject && num2 instanceof NumObject){
        return num1.num+num2.num
    }
}

Enum枚举类型

我们平时会有这种写法:

function getName(status:any){
    if(status===0){
        return 'one'
    }else if(status===1){
        return 'two'
    }else{
        return 'three'
    }
}

console.log(getName(0))

这么写可能有点麻烦,阅读起来还是有点麻烦,这时候可以这么写:

const Status={
    ONE:0,
    TWO:1,
    THREE:3
}

function getName(status:any){
    if(status===Status.ONE){
        return 'one'
    }else if(status===Status.TWO){
        return 'two'
    }else{
        return 'three'
    }
}

console.log(getName(0));

这时候我们的枚举就要上场了:

enum Status{
    ONE,
    TWO,
    THREE
}

function getName(status:any){
    if(status===Status.ONE){
        return 'one'
    }else if(status===Status.TWO){
        return 'two'
    }else{
        return 'three'
    }
}

console.log(getName(1));

一样也可以输出,因为枚举是有对应数字值的,默认从0开始,当然也可以改变:

enum Status{
    ONE=1,
    TWO,//2
    THREE//3
}

也可以进行返查操作:

enum Status{
    ONE,
    TWO,
    THREE
}

function getName(status:any){
    if(status===Status.ONE){
        return 'one'
    }else if(status===Status.TWO){
        return 'two'
    }else{
        return 'three'
    }
}

console.log(Status.ONE,Status[1]);

七、泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

在函数中使用

我们先编写一个函数:

function add(one:string,two:string){
    return `${one}${two}`
}

console.log(add('a','b'))

这时候我们希望参数更加灵活一些,两个参数为number或者string:

function add(one:string|number,two:string|number){
    return `${one}${two}`
}

console.log(add('a','b'))

这么书写有些麻烦,这时候就可以使用泛型:

function add<T>(one:T,two:T){
    return `${one}${two}`
}

console.log(add&lt;string&gt;('a','b'))

在使用中,泛型通常用 <T> 来进行表示。泛型可以有多个吗?当然可以:

function add<T,P>(one:T,two:P){
    return `${one}${two}`
}

console.log(add<string,number>('a',2))

同时泛型也支持类型推断:

function add<T,P>(one:T,two:P){
    return `${one}${two}`
}

console.log(add<string,number>('a',2))

在类中使用

首先先写一个类,且接受参数为一个数组,数组里面存放string类型的数据:

classList {
    constructor(private list:string[]) {};
    getItem(index:number):string{
        return this.list[index]
    }
};

const list=new List(['girl','boy','wom']);
console.log(list.getItem(1))

这时候如果我们传递数组时候里面又想放数字怎么办:

classList {
    constructor(private list:string[]|number[]) {};
    getItem(index:number):string|number{
        return this.list[index]
    }
};

const list=new List(['girl','boy','wom']);
console.log(list.getItem(1))

这么写就比较复杂了,这时候就可以使用泛型来简化我们的代码:

class List<T>{
    constructor(private list:T[]) {};
    getItem(index:number):T{
        return this.list[index]
    }
};

const list=new List(['girl','boy','wom']);
console.log(list.getItem(1))

发现上面的代码没有报错?因为类型推论,所以不会报错,严格意义上应该new的时候加上类型:

const list=new List<string>(['girl','boy','wom']);

还有一种场景,传递过来是一个数组对象,这时候可以通过继承来解决:

interfacePeople{
    name:string
}

class List<T extendsPeople>{
    constructor(private list:T[]) {};
    getItem(index:number):string{
        return this.list[index].name
    }
};

const list=new List(
    [
        {name:'boy'},
        {name:'girl'}
    ]
);
console.log(list.getItem(1))

泛型约束

上面例子中,泛型可以为任意值,但有时候我们希望还是能稍微约束一下:

function list<T extends number|string>(name:T){
    return `${name}`
}

console.log(list<number>(1))

class  List<T extends number|string> {
    constructor(private list:T[]) {

    };
    getItem(index:number):T{
        return this.list[index]
    }
}

const list=new List([1,2])

console.log(list.getItem(0))

八、Namespace命名空间

新建一个ts项目

  • 首先,我们建立一个项目文件,然后npm init -y生成package.json文件,然后再tsc -init生成ts配置文件。
  • 在根目录下新建index.html文件,再建立一个src和bulid目录,在src目录下新建一个index.js。
  • 配置tsconfig.json文件,设置入口和输出目录(outDir和rootDir)。

打开index.html,引入js文件:

<script src="./build/index.js"></script>

在新建的ts文件中随便写点什么:

console.log('hello');

然后tsc编译一下,打开控制台,我们就可以看到了

编写一个小组件

在我们刚刚建立的index.ts文件,写一个header、content和footer组件:

classHeader {
    constructor() {
        const elem = document.createElement("div");
        elem.innerText = "This is Header";
        document.body.appendChild(elem);
    }
}

classContent {
    constructor() {
        const elem = document.createElement("div");
        elem.innerText = "This is Content";
        document.body.appendChild(elem);
    }
}

classFooter {
    constructor() {
        const elem = document.createElement("div");
        elem.innerText = "This is Footer";
        document.body.appendChild(elem);
    }
}

classPage {
    constructor() {
        new Header();
        new Content();
        new Footer();
    }
}

然后在index.html中加一行js代码:

<script>new Page();</script>

这时候我们可以看到内容正常输出,但有一个问题,我们的Header、content和footer都暴露了出来,并不是只暴露一个page,这时候我们的命名空间就派上了用场:

命名空间

命名空间声明的关键词是namespace 比如声明一个namespace Home,需要暴露出去的类,可以使用export关键词,这样只有暴漏出去的类是全局的,其他的不会再生成全局污染了。

namespace Home {
classHeader {
    constructor() {
        const elem = document.createElement("div");
        elem.innerText = "This is Header";
        document.body.appendChild(elem);
    }
}

classContent {
    constructor() {
        const elem = document.createElement("div");
        elem.innerText = "This is Content";
        document.body.appendChild(elem);
    }
}

classFooter {
    constructor() {
        const elem = document.createElement("div");
        elem.innerText = "This is Footer";
        document.body.appendChild(elem);
    }
}

export classPage {
    constructor() {
        new Header();
        new Content();
        new Footer();
        }
    }
}

这么写也是比较麻烦,因为我们需要引入两个文件,我们可以通过配置来让他成为一个文件,打开tsconfig.json,找到这一行:

"module":"commonjs"
//修改为:
"module":"amd

然后找到这一行:

{
    "outFile": "./build/index.js"
}

子命名空间

如果在刚刚的组件中再写一个会怎么样?

namespace Components {
    export namespace SubComponents {
        export class Test {}
    }

    //someting ...
}

//读取
// Components.SubComponents.Test

ts中使用import

首先建立一个文件compontent.ts,随便写点东西,然后使用export导出:

export classHeader {
    constructor() {
    const elem = document.createElement("div");
    elem.innerText = "This is Header";
    document.body.appendChild(elem);
    }
}

export classContent {
    constructor() {
    const elem = document.createElement("div");
    elem.innerText = "This is Content";
    document.body.appendChild(elem);
    }
}

export classFooter {
    constructor() {
    const elem = document.createElement("div");
    elem.innerText = "This is Footer";
    document.body.appendChild(elem);
    }
}

然后在index.ts中导入一下:

import { Header, Content, Footer } from "./compontent";

export classPage{
    constructor(){
        new Header();
        new Content();
        new Footer();
    }
}

运行tsc编译,打开build中的index.js,可以看到代码是define开头的,这是 amd 规范的代码,不能直接在浏览器中运行,可以在 Node 中直接运行,所以我们还需要借助require.js的支持:

<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.js"></script>

然后在index.html中使用require.js写法:

<script>
require(["page"], function (page) {
    new page.default();
});
</script>

本文参考:TypeScript基础知识整理open in new window