从包类型扩展命名空间

Posted

技术标签:

【中文标题】从包类型扩展命名空间【英文标题】:Extend a namespace from package typings 【发布时间】:2017-11-24 07:56:48 【问题描述】:

我在这里尝试@typings/fullcalendar

/// <reference path="./types/fullcalendar" />

import * as fullcalendar from 'fullcalendar';
import  TimeGrid  from 'fullcalendar';

// TimeGrid and fullcalendar.views are used then

原文打字见here。

而 fullcalendar-custom.d.ts 是

import * as FC from 'fullcalendar';

export as namespace FC;

declare class TimeGrid  prepareHits() 

declare let views: any;

这会导致类型错误,因此很明显fullcalendar 命名空间没有正确扩展:

TS2305:模块 '".../node_modules/@types/fullcalendar/index"' 没有导出的成员 'TimeGrid'。

TS2339:类型“typeof”上不存在属性“views”.../node_modules/@types/ 完整日历/索引"'。

这应该如何以正确的方式完成?

考虑到types 目录是在typeRoots 中指定的,这里可以避免reference 指令吗?

该应用程序与 Webpack 和 awesome-typescript-loader 捆绑在一起,因此其行为可能与其他编译方法不同。在某些时候类型在 IDE 检查 (WebStorm) 中似乎没问题,但在编译时仍然出现类型错误。

【问题讨论】:

【参考方案1】:

我们可以在非声明.ts 中导入命名空间,然后将其再次导出为扩展类型:

// custom-fc.ts : enhances declaration of FC namespace
import * as origFC from "fullcalendar";

declare namespace Complimentary 
    class TimeGrid 
        prepareHits(): void;
    
    let views: any;


// apply additional types to origFc and export again
export const FC: (typeof Complimentary & typeof origFC) = origFC as any;

 

// use-fc.ts : consumer of extended declaration
import  FC  from "./custom-fc";

console.log(FC.TimeGrid);
console.log(FC.views);

(这与您的情况有所不同,因为我使用的是 @types/ 包和 webpack ts-loader,但您应该能够做类似的事情。)

【讨论】:

谢谢,我尽量避免重新导出,因为我没有在我扩展类型的地方扩展包本身,但它可以完成工作。我最终得到了declare class Complimentary ... export default &lt;typeof origFC &amp; ExtendedFC&gt;&lt;any&gt;origFC; 。命名空间对 TS 来说是可以的,但会导致 IDE 出现一些类型问题。 不能在 TS3 中工作?导出默认 original;不能使用命名空间“原始”作为值。不能将命名空间“扩展”用作类型。 有没有办法在不更改名称的情况下扩展原始命名空间?【参考方案2】:

您可以轻松扩展“fullcalendar”或任何其他 TypeScript 命名空间。

示例:创建 fullcalendar-extension.d.ts 文件

/// <reference path="<path-to-typings-dir>/fullcalendar/index.d.ts" />

declare module 'fullcalendar' 

  export interface TimeGrid 

    customField: string;

    customMethod(arg1: number, arg2: boolean): boolean;

    prepareHits();
  

  namespace customNamespace 

    export interface AnotherTimeGrid 
      customField1: string;
      customField2: boolean;
    
  

注意:确保该文件被 TypeScript 编译器拾取。

使用扩展模块中新定义的类型。

// one way
import  TimeGrid  from 'fullcalendar';

const timeGrid: TimeGrid;

// second way
import * as fc from 'fullcalendar';

const timeGrid: fc.TimeGrid;
const anotherTimeGrid: fc.customNamespace.AnotherTimeGrid;

有关模块和命名空间的更多信息,您可以查看 Modules 和 Namespaces 上的 TypeScript 文档并使用它们 together。

干杯!

【讨论】:

但是customNamespace 应该如何以“一种方式”被识别呢?我正在尝试做import TimeGrid from 'fullcalendar'; class CustomGrid extends TimeGrid ... ,但 TimeGrid 及其方法无法从界面中识别(而且我看不到它在“第二种方式”中的工作方式)。 在给定的示例中,TimeGrid 被定义为一个接口,CustomGrid 类只能实现它。如果 TimeGrid 是可以在 fullcalendar 库中扩展的类,则在 fullcalendar-extension.d.ts 文件中将其声明为类。用 'class' 替换 'interface' 关键字很简单。 关于导入,由于ES6模块的对象解构能力有限,'customNamespace'只能按照'第二种方式'使用。【参考方案3】:

使用最新的 TypeScript v4,当我在将外部文件和库与 Svelte 组合时遇到问题时,我这样做了:

// root/my-declarations.d.ts

import  SomeNameSpace as SomeNameSpaceOriginal from '../../any-relative-path/to/ts-file';
import SvelteComponentDev from 'svelte/internal';

declare namespace SomeNameSpaceExtended 
    function setValue(value:any):any
    let UI:SvelteComponentDev
    let abc:string


declare global 
    //this will add new items to top-level (root)
    //note: in my case, only importing "as SomeNameSpaceOriginal" gave me correct typing later using our new extended global SomeNameSpace
    let SomeNameSpace: typeof SomeNameSpaceOriginal & typeof SomeNameSpaceExtended;

    //this will add new items to window:
    interface Window 
        elementInside:'window.elementInside'
    

Upvoted Answer 与 https://***.com/a/63973683 相结合,最能理解如何扩展事物。

【讨论】:

以上是关于从包类型扩展命名空间的主要内容,如果未能解决你的问题,请参考以下文章

Swift 命名空间形式扩展的实现

实现函数调用的命名空间

9.类名与命名空间

Spring-IOC学习笔记-04扩展命名空间

Socket.io 命名空间限制以及如何扩展它

命名空间“System.Configuration”中不存在类型或命名空间名称“ConfigurationManager”