网站搜索

TypeScript 单文件编译不包括接口


背景

我需要为现有的 Javascript 包在 Typescript 中创建一个适配器。这需要为现有包创建定义文件,并编写一些基于这些接口的类。我将需要在多个项目中重用生成的 Typescript 包。

为了创建这个包,我使用的是 C# 类库项目,但没有定义任何 C# 类型,我仅仅将其用于 Typescript。Typescript 编译器被设置为输出一个单独的 *.js 和 *.d.ts 文件。

问题

当我编译时,在我的 *.ts 文件中定义的类可以在输出的 *.d.ts 文件中找到,但在 *.d.ts 文件中定义的任何接口不会显示在输出中。我的类中的几个方法使用这些接口作为返回类型,因此当我尝试重用该包时,会出现“找不到名称 'xxx'。”的编译器错误,因为这些接口缺失。

一些代码:

这是正在消失的接口之一:

declare module IS_Interface
{
    export interface Iterable<T>
    {
        hasMoreElements(): boolean;
        nextElement(): T;
    }
}

这个项目中的所有 Typescript 文件都位于 IS_Interface 模块中。声明文件以 declare module IS_Interface 开头,而 *.ts 文件只是以 module IS_Interface 开头。

这是一个使用该接口的类(它会出现在 *.d.ts 文件中):

module IS_Interface
{
    export class IterableUtil
    {
        static ToArray<T>(iterable: Iterable<T>) : Array<T>
        {
            let result = new Array<T>();

            while(iterable.hasMoreElements())
            {
                result.push(iterable.nextElement());
            }

            return result;
        }
    }
}

这是我的编译器设置:

{PropertyGroup Condition="'$(Configuration)' == 'Debug'"}
    {TypeScriptNoImplicitAny}True{/TypeScriptNoImplicitAny}
    {TypeScriptOutFile}bin/debug/IS_Adapter.js{/TypeScriptOutFile}
    {TypeScriptGeneratesDeclarations}True{/TypeScriptGeneratesDeclarations}
    {TypeScriptNoEmitOnError}True{/TypeScriptNoEmitOnError}
{/PropertyGroup}

我项目文件中的所有 TypeScript 和声明文件都在 “TypeScriptCompile Include” 标签中。

问题

  • 有没有办法从 TypeScript 编译中输出接口?
  • 这不是 TypeScript 接口的预期使用方式吗?
  • 有没有一种更简单的方法来创建一个不需要 C#/VB 项目的 Typescript 包?
  • 我不应该像这样将模块拆分到不同的文件中吗?模块的部分内容有声明而部分没有,这重要吗?

更新

@Martin - 我已经移除了模块声明。我对 Typescript 中的命名空间/模块解析非常困惑。模块主要是物理分组还是逻辑分组?还是两者兼而有之?我大部分经验是在 C# 的桌面/服务器编程,其中程序集与命名空间有明确的区别。

这里有一些简化的代码:

注意,在我的输出 *.d.ts 文件中,我仍然有我的导入语句,但它是一个错误的引用。

MyInterface.d.ts:

export interface Iterable<T>
{
    hasMoreElements(): boolean;
    nextElement(): T;
}

MyConsumer.ts:

import { Iterable } from "./MyInterface";

export class Thing
{
    Name: string;
    Thing: Iterable<number>;
}

我在生成的 .d.ts 文件中仍然没有得到接口。

解决方法

这是我丑陋的变通方法。它包括一个预构建命令和一个作为后构建命令运行的exe。它不漂亮,但到目前为止似乎有效。

预构建:(仅仅是为了在重建时不将之前的版本与自身合并)

DEL "$(TargetDir)*.d.ts"

后期构建 :

"$(SolutionDir)\TypeScriptDefMerge\bin\Debug\TypeScriptDefMerge.exe" "$(ProjectDir) " "MyTargetFile.d.ts"

可执行文件(EXE):

using System;
using System.IO;
using System.Linq;

namespace TypeScriptDefMerge {
    class Program {
        // 1st argument must be the project directory path.
        // 2nd argument must be the target file name.
        static void Main(string[] args) {
            Console.Write(args.Length);
            Console.Read();

            if (args == null) throw new ArgumentNullException("args");
            if (args.Length != 2) throw new ArgumentException("Must have exactly 2 arugments.");

            var projectDir = args[0];
            var outputName = args[1];

            //Source definition files must be in this subdirectory
            //I use "\Internal" for my scripts and "\External" for 3rd-party scripts
            var sourceDir = projectDir + "\\Scripts\\Internal";

            //Output must be in this subdirectory
            var outputDir = projectDir + "\\bin\\Debug";

            var temp1 = outputDir + "\\TEMP1.d.ts";
            var temp2 = outputDir + "\\TEMP2.d.ts";

            File.Delete(temp1);
            File.Delete(temp2);

            //Create temp file holding contents of each source file
            CreateMergedFile(temp1, GetDefinitionFiles(sourceDir));

            //Create temp file holding the contents of the 1st temp file, and the normal output file
            CreateMergedFile(temp2, GetDefinitionFiles(outputDir));

            //Delete temporary files
            foreach (var f in GetDefinitionFiles(outputDir).Where(x => x != temp2)) {
                File.Delete(f);
            }

            //Rename final file
            File.Move(temp2, outputDir + "\\" + outputName);
        }

        //Get all definition files in the given directory
        static string[] GetDefinitionFiles(string directory) {
            return Directory.EnumerateFiles(directory)
                .Where(x => x.EndsWith(".d.ts"))
                .ToArray();
        }

        //Concatenate all the given files to a new file at the given path.
        static void CreateMergedFile(string outputPath, string[] sourcePaths) {
            using (var temp = File.CreateText(outputPath)) {
                foreach (var f in sourcePaths) {
                    temp.Write(File.ReadAllText(f));
                    temp.WriteLine();
                }
            }
        }
    }
}

说真的,一定有比这更好的办法。难道没预计到人们会想重用接口吗?如果你能想出什么没那么笨拙的办法,请告诉我。

另外,请注意,这并不承认“从项目中排除”,而是完全基于目录。