65.9K
CodeProject 正在变化。 阅读更多。
Home

10 天学会 Angular - 第 1 天 - 第 2 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (17投票s)

2017 年 7 月 27 日

CPOL

21分钟阅读

viewsIcon

46161

downloadIcon

2673

一个关于最新版Angular的10天系列 - 在本系列的第2部分中,我们将探讨TypeScript的一些更基本概念。

 

你在这里,这意味着你已经成功完成了本系列的第1天第1部分。在第2部分中,我们将探讨TypeScript的一些更基本概念。

完整系列

  1. 第 1 天 – 第 1 部分
  2. 第 1 天 – 第 2 部分
  3. 第 2 天
  4. 第 3 天
  5. 第 4 天 – 第 1 部分
  6. 第4天 - 执行技巧
  7. 第 4 天 – 第 2 部分
  8. 第 4 天 – 第 3 部分
  9. 第 5 天(即将推出)
  10. 第 6 天(即将推出)
  11. 第 7 天(即将推出)
  12. 第 8 天(即将推出)
  13. 第 9 天(即将推出)
  14. 第 10 天(即将推出)

目录

演示8 – 类

TypeScript使我们能够实现面向对象编程。它内置了对类和大多数面向对象特性的支持。让我们快速演示一下。

带有简单变量和函数的类示例

尝试下面的代码。

class Customer{
     CustomerName:String;
    Address:String;
    Save():void{
       console.log('Customer Saved:'+
    this.CustomerName+"-"+this.Address);
    }
}
let c:Customer=new Customer();
c.CustomerName="Customer1";
c.Address="Address1"
c.Save();
let c1:Customer=new Customer();
c1.CustomerName="Customer1";
c1.Address="Address1"
c1.Save();

编译代码并使用node执行生成的JavaScript代码。

输出将如下所示。

访问修饰符示例

我们可以借助访问修饰符轻松决定类的哪些成员将暴露在类外部,哪些成员不会。默认访问修饰符是“public”。

class Customer{
    private CustomerName:String;
    public Address:String;
    Save():void{
       console.log('Thanks');
    }
}
let c:Customer=new Customer();
console.log(c.Address);
console.log(c.CustomerName);

编译将发出以下错误。

带有属性的类示例

属性只是带有getter和setter逻辑的变量的包装。

尝试下面的代码。

class Customer{
    private _age:number;
    get age():number {
        return this._age;
    }
    set age(theAge:number) {
        console.log('new age received '+theAge)
        if(theAge<0 || theAge>100){
            throw "Invalid Age";
        }
        this._age = theAge;
        console.log('new age is set '+theAge)
    }
}
let c:Customer=new Customer();
c.age=55;
c.age=-65;

使用“tsc classExample.ts”命令编译它,您将收到以下错误。

TypeScript默认配置为生成ES3代码。

为了支持属性,至少需要“ES5”。

使用“target”标志重新编译相同的代码,如下所示。

tsc classExample.ts --target es5

这次它将编译。现在使用node执行生成的JS文件。输出将如下所示。

如您所见,第一个成功运行,而第二个生成了运行时错误。

带有构造函数的类示例

构造函数是每个面向对象程序员都熟悉的功能。

它是一种特殊类型的方法,与类名相同,在类初始化时会自动调用。

让我们快速演示一下。

class Customer{
    CustomerName:String;
    Address:String;
    constructor(){
       console.log('Customer new object created');
    }
}
let c:Customer=new Customer();
let c1:Customer=new Customer();

编译并执行,输出将如下所示。

带有简写属性的类示例

如果您是面向对象的程序员,您一定做过类似下面的事情。

class Customer{
    CustomerName:String;
    Address:String;
    constructor(n,a){
        this.CustomerName=n;
        this.Address=a;
    }
}
let c:Customer=new Customer("Sukesh","Mumbai");
let c1:Customer=new Customer("Ganesh","Bengalore");

console.log(c.CustomerName + "-"+c.Address);
console.log(c1.CustomerName + "-"+c1.Address);

在构造函数内部,类成员只是用构造函数参数初始化。

编译并执行,输出将简单地如下所示。

相同的代码可以借助简写属性重新编写,如下所示。

class Customer{
    constructor(public CustomerName:string;
    public Address:string;){
    }
}
let c:Customer=new Customer("Sukesh","Mumbai");
let c1:Customer=new Customer("Ganesh", "Bangalore");

console.log(c.CustomerName + "-"+c.Address);
console.log(c1.CustomerName + "-"+c1.Address);

编译并执行上述代码将生成完全相同的输出。

只需在构造函数中参数名称前指定访问修饰符,它就变成了属性。

“public”关键字表示它们是公共属性,如果需要,我们也可以将其设为私有。

继承示例

没有继承,面向对象编程是不完整的。

让我们快速演示一下。查看下面的代码。

class Customer{
    CustomerName:string;
    protected Address:string;
}

class GoldCustomer extends Customer{
    Discount:number;
    constructor(a:string){
        super();
        this.Address=a;
    }
}

let c:GoldCustomer=new GoldCustomer("Mumbai");
c.CustomerName="Sukesh";
c.Discount=100;

console.log("CustomerName:"+c.CustomerName);
console.log("Discount:"+c.Discount);
console.log("Address:"+c.Address);

编译它,您将收到以下错误。

除了public和private,protected是第三个访问修饰符。

protected变量只能在定义的类和基类中访问。

将“Address”的访问修饰符更改为“public”并重试。(您甚至可以尝试相同的示例,不指定任何访问修饰符,因为默认访问修饰符是public。)

这次它将编译。使用node执行它,输出将如下所示。

演示9 – 模板字符串

在TypeScript中,借助模板字符串,字符串拼接变得容易得多。

它将通过一个特殊的符号,即反引号(`)来完成。

查看下面的代码。

let a:string="ValueA";
let b:string="ValueB";
let c:number=55;
let text:string=`Value of a+b is ${a}${b}
Value of c is ${c}`
console.log(text);

注意

用于表示字符串的符号是反引号(`)。位于键盘上的Esc键正下方。

不要与单引号(')混淆。

编译并执行。输出将如下所示。

演示10 – 箭头函数

在JavaScript世界中,将函数作为参数传递是一种非常常见的做法。

TypeScript提供了一种非常方便的方式来定义无状态函数,称为箭头函数。无状态函数是指没有自己状态的函数,它将共享其定义所在外部函数的作用域。简单来说,箭头函数内部的“this”关键字指向词法作用域(外部函数的作用域)。

仍然感到困惑?让我们使用JavaScript的“setTimeout”方法进行快速演示。setTimeout函数简单地期望另一个方法作为参数,并在指定毫秒数后调用该函数。

让我们首先使用传统方法来做。

创建一个名为“arrowFunctions.ts”的新TypeScript文件,其中包含以下代码:

class Customer{
    IsSaved:boolean=false;
     Save(f):void {
        setTimeout(function() {
            this.IsSaved=true; //this refers to the instance of current anonymous function (not customer class)
            f();
        }, 100);
    }
}

let e:Customer=new Customer();
e.Save(function(){
    console.log(e.IsSaved);
});

编译并执行它,您将在输出中看到“false”。正如我之前所说,“this”指向传递给“setTimeout”函数的匿名函数的实例。此类问题可以使用箭头函数轻松解决。

使用箭头函数重写相同的代码,如下所示。

class Customer{
    IsSaved:boolean=false;
     Save(f):void {
        setTimeout(()=>{
            this.IsSaved=true;
            f();
        }, 100);
    }
}

let e:Customer=new Customer();
e.Save(function(){
    console.log(e.IsSaved);
});

编译并执行它。输出将是“true”。

箭头函数语法也可以用于函数声明,使其类型安全。

查看下面的代码片段。

...
Save(f:()=>void):void {
…
}

这意味着,save函数期望一个函数类型的参数(无参数且返回类型为void)。

演示11 – 接口和抽象类

没有接口和抽象类,面向对象编程是不完整的。

创建一个名为“looselyCouping.ts”的新TypeScript文件,其中包含以下代码。

interface ILogger{
    LogError(e:string):void;
    SendEmailLog(s:string):boolean;
}

abstract class AbstractServiceInvoker{
    Save(){
        let ServiceURL:string=this.GetServiceURL();
        let Credentials="UserName:abcd,password:123";
        let data:string=this.getData();
       
        //Logic for invoking service is written here. 
        let Result:number=5;  //Let’s assume that it is the value 
        //returned by service

        this.setData(Result);
    }
    abstract GetServiceURL():string;
    abstract getData():string;
    abstract setData(n:number):void;
}

如您所见,我们有一个接口ILogger和一个抽象类AbstractServiceInvoker。

现在首先让我们创建接口的实现。

class Logger implements ILogger{
    LogError(e:string):void{
        console.log(`error ${e} logged`);
    }
    SendEmailLog(s:string):boolean{
        console.log(`message ${s} sent in email`);
        return true;
    }
}

Logger类中必须定义LogError和SendEmailLog方法,否则编译器将发出错误。

现在让我们看看抽象类的实现。

class CustomerService extends AbstractServiceInvoker{
    GetServiceURL():string{
        return "http://someService.com/abcd"
    }
     getData():string{
        return "{CustomerName:'a',Address:'b'}"
     }
     setData(n:number):void{
        console.log(`Success:${n}`)
     }
}

派生类中必须定义所有抽象方法,否则编译器将发出错误。

演示12 – 解构

使用此功能,我们可以非常轻松地从数组中提取值到单个变量中。

它还允许我们轻松地将对象的属性值提取到独立的变量中。

创建一个名为“Destructuring.ts”的新TypeScript文件。

首先让我们尝试对象解构

检查以下代码

var a={CustomerName:'A',Address:'b'};
var {CustomerName,Address}=a;
console.log(`${CustomerName}-${Address}`);

编译并执行它。

它将简单地在输出中显示“a-b”

现在让我们尝试数组解构。

查看下面的代码。

var myArray=[1,2,3,4];
var [v1,v2,v3]=myArray;
console.log(`${v1}-${v2}-${v3}`);

编译并执行它。

它将简单地在输出中显示“1-2-3”

演示13 – 命名空间

在第1部分中,我们看到了JavaScript命名空间的实际应用。我们理解了它的重要性以及实现它的逻辑方式。

现在,在TypeScript中,无需担心编写IIFE或任何其他逻辑来实现命名空间。我们在这里内置了对命名空间的支持。

查看下面的代码。

namespace MathsExample{
    export function add(x, y) {
        console.log(x+y);
    }
    function sub(x, y) {
        console.log(x-y);
    }
    export class MyClass{}
}
MathsExample.add(1,2);

在上面的示例中,只有“add”函数和“MyClass”类在命名空间外部可用。

如果您编译代码,您将发现JavaScript中生成了一个自定义的IIFE逻辑,

var MathsExample;
(function (MathsExample) {
    function add(x, y) {
        console.log(x + y);
    }
    MathsExample.add = add;
    function sub(x, y) {
        console.log(x - y);
    }
    var MyClass = (function () {
        function MyClass() {
        }
        return MyClass;
    }());
    MathsExample.MyClass = MyClass;
})(MathsExample || (MathsExample = {}));
MathsExample.add(1, 2);

这是一个简单的JavaScript代码

。要进行测试,请打开命令提示符并尝试以下命令。

node tsNameSpaceExample.js

演示14 – 模块

就像命名空间一样,TypeScript对模块有现成的支持。

当一个TypeScript文件至少包含一个带有export关键字的全局成员时,该文件将成为一个TypeScript模块。模块名称将与文件名相同,但不带扩展名。

TypeScript编译器将以“CommonJS”格式生成输出。这是默认的模块格式器。

让我们看一个例子。

步骤1 – 设置文件夹

创建一个名为“TsModuleSample”的新文件夹,并在其中创建两个TypeScript文件。Reusable.ts和Index.ts

步骤2 – 定义Reusable.ts

将以下代码放入Reusable.ts中

function Add(...values:Array<number>):number{
    let sum:number=0;
    values.forEach(e=>{
        sum+=e;
    })
    return sum;
}

export function AddTwoNumbers(x:number,y:number){
    return Add(x,y);
}

步骤3 – 定义Index.ts

将以下代码放入Index.ts中

import {AddTwoNumbers} from "./reusable"
console.log(AddTwoNumbers(11,12));

注意:如果TypeScript文件名为“A.ts”或“a.ts”,则Typescript模块名为“a”。

步骤4 – 编译它

现在有趣的地方来了。编译index.ts。它将自动编译Reusable.ts

tsc index.ts

打开“reusable.js”文件。它将如下所示。

"use strict";
exports.__esModule = true;
function Add() {
    var values = [];
    for (var _i = 0; _i < arguments.length; _i++) {
        values[_i] = arguments[_i];
    }
    var sum = 0;
    values.forEach(function (e) {
        sum += e;
    });
    return sum;
}
function AddTwoNumbers(x, y) {
    return Add(x, y);
}
exports.AddTwoNumbers = AddTwoNumbers;

上述代码遵循“CommonJS”模块格式。

注意:默认情况下,TypeScript配置为生成“CommonJS”代码。稍后我们将看到如何更改此配置。

步骤4 – 执行它

使用node命令执行生成的“index.js”。

node index.js

输出将是“23”。

您可能想知道如何在没有模块加载器的情况下成功执行。

Node完美支持“CommonJS”模块格式器。它能很好地理解它。

如果模块格式器更改为AMD或其他,我们将在没有模块加载器的情况下无法执行它。

演示15 – TypeScript配置

TypeScript编译器可以根据我们的需要进行配置。

步骤1 – 创建配置文件

首先创建一个名为“TSConfigSample”的新文件夹,并在其中创建一个名为“tsconfig.json”的新文件。此文件存在于目录中表明它是我们TypeScript源代码项目的根目录。

步骤2 – 创建示例文件

创建一个新的TypeScript文件“configTest.ts”。将以下代码放入其中。

class Customer{
     Save():void{
        console.log("Thanks");
    }
}

步骤3 – 编译

打开命令提示符,导航到“TSConfigSample”文件夹并简单尝试以下命令。

tsc

当tsc不带文件名使用时,它将编译当前文件夹中的所有文件。

您将收到一个非常奇怪的错误。

将以下行放入tsConfig.json文件并重复编译过程。

{}

现在编译将正常工作,输出文件将如下所示。

var Customer = (function () {
    function Customer() {
    }
    Customer.prototype.Save = function () {
        console.log("Thanks");
    };
    return Customer;
}());

步骤4 – 更改配置

默认情况下,TypeScript配置为生成“ES5”代码。它可以设置为ES2015。为此,将以下内容放入“tsConfig.json”文件中。

{
    "compilerOptions": {
        "target": "es2015"
    }
}

现在再次编译它。(确保使用“tsc”而不是“tsc filename.ts”)。

这次输出文件将如下所示。

class Customer {
    Save() {
        console.log("Thanks");
    }
}

探索更多编译器选项

module

此选项使我们能够决定模块格式器。默认选项是“CommonJS”,但也可以更改为“AMD”或其他可用选项。

target

我们期望的目标文件的版本是什么。默认为“ES3”,但可以将其设置为更高版本。

noEmitOnError

默认值为false。如果设置为true,当存在编译错误时,TypeScript将不会生成目标JS文件。

allowUnreachableCode

默认为false。当设置为true时,不报告无法访问的代码错误。

查看下面的代码。

class Customer{
       Save():boolean{
       if(true){
        return true;
       }
       return false;
    }
}

如果“allowUnreachableCode”被显式设置为true,则上述代码将编译失败

noImplicitAny

默认值为false。当编译器无法根据变量的使用方式推断变量类型时,编译器将类型视为“any”。例如,查看下面的代码。

Save(s):void{
        console.log(s+1);
    }

在上述情况下,未提及参数类型。“类型推断”根据第一次赋值决定数据类型,在上述情况下,第一次赋值发生在运行时。因此,数据类型将设置为“any”

将“noImplicitAny”设置为true强制我们指定数据类型。每当出现上述情况时,就会抛出错误。

suppressImplicitAnyIndexErrors

您听说过JavaScript世界中的方括号表示法吗?查看下面的JavaScript代码。

var a={"key1": "value1", "key2": "value2"};
var myKey= "key1";
console.log(a[myKey]); // display value1

上述JavaScript代码是有效的JavaScript代码。

现在让我们尝试下面的TypeScript代码,其中“noImplicitAny”设置为true。

class Customer{
    CustomerName:string;
    Address:string;
       Save():boolean{
       if(true){
        return true;
       }
       return false;
    }
}

let c:Customer=new Customer();
c.CustomerName="Sukesh Marla";
c.Address="Mumbai";

let key: string = 'CustomerName';

console.log(c[key]);

编译它,将发生一个奇怪的编译错误。

configTest.ts(23,27): error TS7017: Element implicitly has an 'any' type because type '{ firstKey: string; secondKey: string; thirdKey: string; }' has no index signature.

错误很明显,TypeScript编译器无法推断“c[key]”的数据类型,因此它将被视为“any”。

将“suppressImplicitAnyIndexErrors”设置为true将简单地忽略索引基于访问的“noImplicitAny”。

sourceMap

默认值为false。如果设置为true,编译时将生成一个额外的文件,扩展名为“.js.map”。拥有这些文件可以让外部工具(如Visual Studio、chrome开发者工具等)在运行时调试TypeScript而不是JavaScript。

我们将在使用Angular时看到演示。

removeComments

默认值为false。如果设置为true,编译器将不会在生成的JavaScript文件中发出注释。

experimentalDecorators

TypeScript有一个名为装饰器的特性,它将用于向类、方法、属性等添加额外信息。

如果您有C#背景,它们类似于C#中的属性,如果您有Java背景,它们类似于注解。

在TypeScript中,装饰器可以附加到类/方法/属性/变量/参数等。

语法非常简单。假设我们有一个类装饰器“myClassDecorator”,一个函数装饰器“myFunctionDecorator”,一个属性装饰器“myPropertyDecorator”和一个参数装饰器“myParameterDecorator”。应用它们的代码片段将如下所示。

@myClassDecorator()
class Customer{
    @myPropertyDecorator()customerName:string;
    constructor(@myParameterDecorator()s:string){
    }
    @myFunctionDecorator()
    Save():void{
    }
}

注意:在本课程中,我们不会创建自定义装饰器,这将超出本系列的范围。我们将更专注于Angular。但我们肯定会在整个课程中使用许多现成的装饰器。

装饰器目前是TypeScript中的实验性功能。它可能会在TypeScript的更高版本中更改。为了在项目中使用装饰器,我们需要在“tsconfig.json”文件中将“experimentalDecorators”设置为true。

为了创建Angular应用程序,将此标志设置为true是必须的。借助装饰器,我们向类/函数/属性等添加元数据。Angular在执行状态后期使用此元数据。

emitDecoratorMetadata

为了使用装饰器,我们还需要将此标志设置为true。它强制Typescript编译器将类型信息与元数据一起保存。

装饰器是一个在与Angular等第三方框架一起工作时非常有用的功能。我们将来会注意到许多框架利用装饰器。借助装饰器,我们将为我们的方法/类/属性等提供一些额外的元数据,这些元数据将在运行时被框架(或一些自动化代码)使用。

简单地说,“experimentalDecorators”使我们能够在TypeScript应用程序中使用装饰器,而“emitDecoratorMetadata”只是将类型信息添加到因装饰器生成的元数据中。

moduleResolution

在我们之前的一个演示中,我们讨论了TypeScript模块。

“moduleResolution”是编译器用于定位表示导入模块的文件的过程。

它可以设置为两个可能的值之一——经典和node。

当模块设置为“AMD”、“System”或“ES2015”时,“moduleResolution”的默认值将是“classic”。在所有其他情况下,默认值将是“node”

导入语句可以以两种不同的方式编写。

  1. 相对导入 – 以/ 或 ./ 或 ../ 开头的导入

    例如,Import {a} from "./mymodule"

  2. 非相对导入 – 不以任何上述符号开头的导入。

    例如,Import {a} from "mymodule"

假设在以下位置创建了一个TypeScript文件。

"H:\Day 1 Source code\TS Examples\TSConfigSample"

它包含以下两个导入语句

Import {a} from "./mymodule"

Import {b} from "mymodule2"

经典

第一次导入将导致以下查找。

  1. H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.d.ts
  2. H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.ts

第二次导入将导致以下查找。

  1. H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule2.d.ts
  2. H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule2.ts
  3. H:\Day 1 Source code\TS Examples\mymodule2.d.ts
  4. H:\Day 1 Source code\TS Examples\mymodule2.ts
  5. H:\Day 1 Source code\mymodule2.d.ts
  6. H:\Day 1 Source code\mymodule2.ts
  7. H:\mymodule2.d.ts
  8. H:\mymodule2.ts

节点

第一次导入将导致以下查找。

  1. H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.d.ts
  2. H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.ts
  3. H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule\somemainfile

    查找3只会发生,

    1. 当前文件夹内有一个名为mymodule的文件夹
    2. my module文件夹包含package.json
    3. package.json将有类型属性设置为某个“somemainfile.ts”或“somemainfile.d.ts”文件。
  4. H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule\Index.ts
  5. H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule\Index.d.ts

第二次导入将导致以下查找

  1. H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2.ts
  2. H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2.d.ts
  3. H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\somemainfile
    (与第一次导入中讨论的package.json和类型概念相同)
  4. H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2\index.ts
  5. H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2\index.d.ts
  6. H:\Day 1 Source code\TS Examples\node_modules\mymodule2.ts
  7. H:\Day 1 Source code\TS Examples\node_modules\mymodule2.d.ts
  8. H:\Day 1 Source code\TS Examples\node_modules\somemainfile
    (与第一次导入中讨论的package.json和类型概念相同)
  9. H:\Day 1 Source code\TS Examples\node_modules\mymodule2\index.ts
  10. H:\Day 1 Source code\TS Examples\node_modules\mymodule2\index.d.ts
  11. H:\Day 1 Source code\node_modules\mymodule2.ts
  12. H:\Day 1 Source code\node_modules\mymodule2.d.ts
  13. H:\Day 1 Source code\node_modules\somemainfile
    (与第一次导入中讨论的package.json和类型概念相同)
  14. H:\Day 1 Source code\node_modules\mymodule2\index.ts
  15. H:\Day 1 Source code\node_modules\mymodule2\index.d.ts
  16. H:\node_modules\mymodule2.ts
  17. H:\node_modules\mymodule2.d.ts
  18. H:\node_modules\somemainfile
    (与第一次导入中讨论的package.json和类型概念相同)
  19. H:\node_modules\mymodule2\index.ts
  20. H:\node_modules\mymodule2\index.d.ts

lib

到目前为止,我们还没有看到我们的全局node模块安装在哪里。它是以下文件夹。

%AppData%\npm\node_modules

在此文件夹中,您将找到为每个已安装的全局node模块创建的一个文件夹。

打开“TypeScript”文件夹。里面有一个名为“lib”的文件夹,其中包含许多TypeScript文件。

如果您检查这些文件的名称,它们大多遵循一个共同的命名约定。所有文件都命名为“*.d.ts”。

“d.ts”文件称为类型定义文件。

类型定义文件是TypeScript文件(命名为“something.d.ts”),其中包含特定库或框架中可用构造的声明。例如,jQuery的类型定义文件包含jQuery函数的声明。

最简单的定义是,TypeScript定义文件是向TypeScript编译器教授新事物的方式。

“lib”文件夹中的文件包含常见JavaScript构造的声明。

例如,它们包含“console.log”、“document”、“Array”、“alert”等的声明。

根据这些定义文件中给出的声明,TypeScript将检查我们的代码并确认我们是否犯了任何错误。例如,“alert”在类型定义文件中声明如下:

declare function alert(message?: any): void;

当我们尝试使用多个参数调用alert函数时,Typescript编译器将发出错误,因为根据声明它只能接受一个参数。

“lib”文件夹中有两个非常重要的文件。它们是包含“ES5”提供的构造声明的“lib.d.ts”和包含ES2015中存在的构造声明的“lib.es6.d.ts”。

(如果您检查这两个文件,您会注意到里面有一些相似的声明。例如,“alert”在这两个文件中都将完全按照上面的方式定义。)

根据我们设置的目标,其中一个文件将自动包含在编译过程中。这些文件只是让我们编写类型安全的代码。

现在假设我们有一个场景,我们希望目标是“ES5”,但我们想使用“ES2015”功能。一旦我们将目标设置为“ES5”,如果我们使用了任何ES2015函数或类,编译器将开始发出错误。

在这种情况下,我们将通过使用“lib”选项来覆盖默认包含。它将让我们手动决定要包含哪些库。

例如,查看下面的“tsconfig.json”文件。

{
    "compilerOptions": {
        "target": "es5",
       "lib": [
           "dom","es2015"
       ]
    }
}

它将简单地包含两个库文件。

  1. lib.dom.d.ts – 它将允许我们使用常见的DOM相关构造,如“console”、“document”、“window”等。
  2. lib.es2015.core.d.ts – 它将允许我们使用ES2015运行时中存在的构造。例如,我们可以使用ES2015 promises。

注意

  • ES2015只是ES6的新名称,所以不要混淆它们。
  • lib.es6.d.ts与lib.es2015.core.d.ts非常不同。
    • 第一个是目标为“ES2015”时的默认包含。它包含JavaScript中几乎所有构造的声明。它包括核心构造(如Array、Boolean等)和DOM构造(如document、window、console等)。
    • 第二个只包含JavaScript核心构造的声明,这就是为什么DOM构造需要包含单独的库的原因。

演示16 – 使用第三方JavaScript库

我们已经讨论了TypeScript编译器根据配置设置加载的默认库。这些库是简单的类型定义文件,其中包含核心JavaScript构造的声明。TypeScript编译器仅基于这些文件确认我们代码的有效性。如果任何构造以无效方式使用,它只会发出错误。

现在的问题是,如何在TypeScript中处理第三方JavaScript库?例如,假设我们想在TypeScript中使用“jQuery”函数。TypeScript将无法识别“$”或“jQuery”中的任何其他函数。使用它们将导致编译错误。

解决方案是类型定义文件。除了JavaScript库文件,我们还必须下载相应的类型定义文件。

让我们一步一步地进行演示。

步骤1 – 设置文件夹

创建一个名为“TypeDefinationExample”的新文件夹,并在其中创建一个名为“jQuerySample.ts”的新TypeScript文件

步骤2 – 下载库和类型定义

打开命令提示符。导航到上述文件夹,并使用“npm”下载jQuery,如下所示。

npm install jquery

注意:npm命令区分大小写。请确保“jquery”按原样编写。

上述命令将下载jQuery库并将其放置在node_modules文件夹中。

接下来使用以下命令下载类型定义文件。

npm install @types/jquery

步骤3 – 编写代码

打开“jQuerySample.ts”文件并编写以下代码。

import * as $ from "jquery"

$(document).ready(()=>{
    $('#MyElement').html("<b>Hello World</b>");
});

(不用担心上面的jQuery代码。我们的目标是Angular而不是jQuery。上面的代码将找到ID为“MyElement”的HTML元素,并将其内部HTML设置为“<b>Hello World</b>”)

步骤4 – 测试代码

在命令提示符中写入以下命令。

tsc jQuerySample.ts

它将导致以下错误。

步骤5 – 解决bug

最新版本的jQuery基于ES2015规范。这意味着我们希望在编译时包含ES2015库。可以通过两种方式完成。

  1. 创建tsConfig.json文件,并使用“lib”选项与“es2015”和“dom”,然后使用tsc编译它
  2. 在命令提示符本身中使用“lib”编译器选项。

对于我们的示例,让我们使用第二种方法。

tsc jQuerySample.ts --lib es2015,dom

现在它将成功编译。

步骤6 – 创建源文件

创建一个名为“Test.html”的新HTML文件,如下所示。

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8" />
    <script src="../../node_modules/systemjs/dist/system.js"></script>
    <script>
       SystemJS.import('./jQuerySample.js');
    </script>
</head>
<body>
     <div id="MyElement">Loading...</div>
</body>
</html>

如您所见,我们借助SystemJs加载了“jQuerySample.js”。

步骤7 – 启动服务器

在命令提示符中,只需写入以下命令即可启动Web服务器。

http-server

(“http-server”是我们之前在某个实验中安装的Node模块。如果您尚未安装,请执行“http-server -g”命令进行安装)

步骤8 – 测试

打开浏览器并浏览“Test.html”。您将收到以下错误。

 

理解问题

在我们的例子中,SystemJs(模块加载器)负责在运行时加载其他文件。这意味着SystemJs无法找到“jQuery”。

 

发生了两件非常奇怪的事情。

  1. “jQuery”是“node_modules”中的一个文件夹。这意味着SystemJs正在尝试加载文件夹。模块加载器用于动态加载JS文件,在我们的例子中它正在尝试加载文件夹。
  2. 其次,“jQuery”文件夹存在于“node_modules”文件夹中,但SystemJs正在根文件夹中搜索它。(仔细检查错误,它说,“Fetch error: 404 Not Found Instantiating http://192.168.0.100:8080/jquery”)。

 

让我们看看生成的“jQuerySample.js”文件。

"use strict";
exports.__esModule = true;
var $ = require("jquery");
$(document).ready(function () {
    $('#MyElement').html("<b>Hello World</b>");
});

 

如果您还记得,我们在TypeScript文件中写了以下语句。

import * as $ from "jquery"

因此生成的JS文件包含以下语句。

var $ = require("jquery")

它应该如下所示。

var $= require("/node_modules/jquery/src/jquery.js")

问题的解决方案

解决方案又是“教导”。您必须告诉SystemJs,加载“jquery”意味着加载“/node_modules/jquery/src/jquery.js”。这可以通过创建SystemJs配置文件来完成。

 

步骤9 – 配置SystemJs

创建一个新的JS文件并将其命名为“system.config.js”。

现在在其中放入以下内容。

(function (global) {
  System.config({
    map: {
      'jquery': '/node_modules/jquery/src/jquery.js'
    },
  });
})(this);

在“Test.html”中,在system.src.js之后和main.js之前包含上述文件,如下所示。

<script src="../../node_modules/systemjs/dist/system.js"></script>
<script src="system.config.js"></script>
<script>
    SystemJS.import('./jQuerySample.js');
</script>

 

步骤9 – 重新检查输出

切换到浏览器并刷新页面。

注意:在使用JavaScript时,有时可能会因为缓存而感到困惑。如果输出不符合预期,请务必清除缓存。

理解问题

花点时间思考一下这个错误的原因。

我们在“jQuerySample”中导入了jQuery。同样地,在“jQuery”中导入了许多其他与jQuery相关的文件,“selector.js”就是其中之一。

如果您打开“/node_modules/jquery/src”文件夹,您会在那里看到“selector.js”。

这意味着这次路径是正确的。那么问题是什么?问题是文件名。

它正在寻找一个名为“selector”的文件,我们没有。我们在“/node_modules/jquery/src”文件夹中有“selector.js”。

这个问题的解决方案

解决方案又是教导。我们必须告诉SystemJs为所有请求添加默认扩展名。

步骤10 – 重新配置SystemJs

向“system.config.js”添加更多设置,如下所示。

(function (global) {
  System.config({
    map: {
      'jquery': '/node_modules/jquery/src/jquery.js'
    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
      '/': {
        defaultExtension: 'js'
      }
    }
  });
})(this);

查看上面新的“packages”部分。

 它包含一个条目,告诉SystemJs为所有对根(“/”)文件夹内文件发出的请求添加“js”扩展名。

 

步骤11 – 重新检查输出

切换到浏览器并刷新页面。

总结

到此,第二部分结束,TypeScript学习也告一段落。
是时候庆祝了。您成功完成了第一天的所有演示。

现在终于到了Angular的时候了。第二天再见。

敬请期待。
 

敬请期待。

我在twitterFacebooklinked in

 

想了解更多关于我的信息。请访问这里

www.sukesh-marla.com

www.justcompile.com

© . All rights reserved.