8

TypeScript对于Duck类型和模块命名空间的应用实战

 1 year ago
source link: https://blog.51cto.com/u_15603561/5577787
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Table of Contents

一.TypeScript 鸭子类型

TypeScript对于Duck类型和模块命名空间的应用实战_javascript

Duck类型是一种动态类型和多态形式。
在这种风格中,对象的有效语义不是通过从特定类继承或实现特定接口来确定的,而是通过“当前方法和属性的集合”来确定的。

var object_name = { 
    key1: "value1", // 标量
    key2: "value",  
    key3: function() {
        // 函数
    }, 
    key4:["content1", "content2"] //集合
}

在duck类型中,重点是对象的行为可以做什么,而不是对象所属的类型。
例如,在不使用duck类型的语言中,我们可以编写一个函数,该函数接受“duck”类型的对象并调用其“walk”和“call”方法。
在使用duck类型的语言中,这样的函数可以接受任何类型的对象并调用其“go”和“call”方法。
如果这些需要调用的方法不存在,则会引发运行时错误。具有这种正确的“go”和“call”方法的任何对象都可以被函数接受。
此行为导致上述表达式,并将此确定类型的方法命名为鸭子类型。

interface IPoint { 
    x:number 
    y:number 
} 
function addPoints(p1:IPoint,p2:IPoint):IPoint { 
    var x = p1.x + p2.x 
    var y = p1.y + p2.y 
    return {x:x,y:y} 
} 
 
// 正确
var newPoint = addPoints({x:3,y:4},{x:5,y:1})  
 
// 错误 
var newPoint2 = addPoints({x:1},{x:4,y:3})

二.TypeScript 命名空间

名称空间最明确的目的之一是解决重复名称的问题。
假设班上有两个叫小明的学生。为了清楚地区分他们,我们必须使用他们名字之外的一些附加信息,例如他们的姓氏(王晓明、李晓明)或他们父母的名字。
命名空间定义标识符的可见范围。一个标识符可以在多个名称空间中定义,它在不同名称空间中的含义是无关的。这样,任何标识符都可以在新名称空间中定义,并且它们不会与任何现有标识符冲突,因为现有定义在其他名称空间中。
typescript中的命名空间由命名空间定义。语法格式如下:

namespace SomeNameSpaceName { 
   export interface ISomeInterfaceName {      }  
   export class SomeClassName {      }  
}

上面定义了一个名称空间somenamespacename。
如果我们需要在外部调用somenamespacename中的类和接口,我们需要向类和接口添加export关键字。
要调用另一个命名空间,语法格式为:

SomeNameSpaceName.SomeClassName;

如果命名空间位于单独的typescript文件中,则应使用三个斜杠///引用它。语法格式如下:

/// <reference path = "SomeFileName.ts" />
namespace Drawing { 
    export interface IShape { 
        draw(); 
    }
}

名称空间支持嵌套,也就是说,可以在另一个名称空间中定义名称空间。

namespace Runoob { 
   export namespace invoiceApp { 
      export class Invoice { 
         public calculateDiscount(price: number) { 
            return price * .40; 
         } 
      } 
   } 
}

三.TypeScript 模块

typescript模块设计有可替换的组织代码。
模块在其自己的范围内执行,而不是在全局范围内,这意味着模块中定义的变量、函数和类在模块外部不可见,除非它们使用导出显式导出。
同样,我们必须导入其他模块通过导入导出的变量、函数、类等。
这两个模块之间的关系是通过在文件级使用导入和导出来建立的。
模块使用模块加载器导入其他模块。在运行时,模块加载器的功能是在执行模块代码之前查找并执行模块的所有依赖项。最常见的JavaScript模块加载器是为node JS和require提供服务。用于web应用程序的js。
此外,还有systemjs和webpack。
模块导出使用关键字export。语法格式如下:

// 文件名 : SomeInterface.ts 
export interface SomeInterface { 
   // 代码部分
}

要在其他文件中使用此模块,需要使用导入关键字进行导入:

import someInterfaceRef = require("./SomeInterface");

TestShape.js 文件代码为:

define(["require", "exports", "./Circle", "./Triangle"], 
   function (require, exports, circle, triangle) {
   
   function drawAllShapes(shapeToDraw) {
      shapeToDraw.draw();
   }
   drawAllShapes(new circle.Circle());
   drawAllShapes(new triangle.Triangle());
});

四.类型脚本声明文件

作为JavaScript的超集,typescript在开发过程中不可避免地引用了其他第三方JavaScript库。
虽然可以通过直接引用调用库的类和方法,但不能使用类型脚本功能,如类型检查。
为了解决这个问题,我们需要删除这些库中的函数和方法体,只保留导出的类型声明。
相反,将生成描述JavaScript库和模块信息的声明文件。
通过引用此声明文件,可以借用typescript的各种功能来使用库文件。
如果我们想使用第三方库,比如jQuery,我们通常会得到一个ID为foo的元素,如下所示:

$('#foo');
// 或
jQuery('#foo');

但在typescript中,我们不知道$jQuery是什么:

jQuery('#foo');

// index.ts(1,1): error TS2304: Cannot find name 'jQuery'.

此时,我们需要使用declare关键字定义其类型,以帮助typescript判断传入的参数类型是否正确:

declare var jQuery: (selector: string) => any;

jQuery('#foo');

declare定义的类型仅用于编译时的检查,并将从编译结果中删除。
上述示例的编译结果是:

jQuery('#foo');

声明文件以 .d.ts 为后缀,例如:

runoob.d.ts

声明文件或模块的语法格式如下:

declare module Module_Name {
}

Typescript导入声明文件语法格式:

/// <reference path = " runoob.d.ts" />
TypeScript对于Duck类型和模块命名空间的应用实战_javascript_02

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK