Typescript自定义工具,转译器,扩展

我想在tsc之前更改/检查.ts文件,它将开始其转译过程,类似于Roslyn为C#提供的。

这对静态类型检查很有用。 例如,正确和短的Immutable Record实现需要在编译时进行代码更改/静态检查,因为它可以使用Flow完成:

@Record(Person)
interface IPerson {  
  givenName: string;
  familyName: string;
}

然后自定义tsc transpiler可以修改代码来:

interface IPersonParams {  
  givenName?: string;
  familyName?: string;
}
@Record()
class Person {  
  private readonly __givenName;
  private readonly __familyName;
  constructor(init) {
    this.__givenName = init.givenName;
    this.__familyName = init.familyName;
  }
  get givenName() {
    return this.__givenName;
  }
  get familyName() {
    return this.__familyName;
  }
  update(update: IPersonParams) {
    // ignore the bug when undefined param is passed
    return new Person({
      givenName: update.givenName || this.__givenName,
      familyName: update.familyName || this.__familyName
    });
  }
}

立即看到自定义编译错误会很好,因为现在使用Visual Studio和Visual Studio Code完成特殊的tsc watch ,而不是作为webpack绑定或自定义gulp任务的一部分。 有一个用于Typescript的API,但是如何使它与VS/VS Code/Atom tsc无缝协作?


用示例更新

目标只是写

@Record(Person)
interface IPerson {  
  givenName: string;
  familyName: string;
}
  • 如前所示, Person类将根据接口IPerson自动生成。

  • 可以实例化对象:

    let instance = new Person({givenName: "Emma", familyName: "Watson"});

    任何不正确的属性都会引发编译错误:

    let instance = new Person({nonExistedProperty: "Emma"}); //error

    错误:属性'nonExistedProperty'在类Person构造函数中不存在;
    错误:属性'givenName'在类Person构造函数中是必需的;
    错误:属性'familyName'在类Person构造函数中是必需的;

  • 现有对象应该能够部分更新

    let instance = new Person({givenName: "Emma", familyName: "Watson"}); instance.Update({givenName: "Luise"});

    instance.givenName === "Luise"; //TRUE;
    instance.familyName === "Watson"; //TRUE;

  • 所有属性都是只读的

    let instance = new Person({givenName: "Emma", familyName: "Watson"}); instance.givenName = "John"; //error

    错误:属性'givenName'是只读的;

  • Equals方法是自动生成的。 它可以基于散列或其他任何东西,但应该快速工作并提供深入检查。

    let instance1 = new Person({givenName: "Emma", familyName: "Watson"});
    let instance2 = new Person({givenName: "Emma", familyName: "Watson"});

    instance1.Equals(instance2); //TRUE

    它也可能有一个控制创建实例的位置,如果内部字典中存在具有相同参数的记录,则它只是返回对该对象的引用:

    let instance1 = new Person({givenName: "Emma", familyName: "Watson"});
    let instance2 = new Person({givenName: "Emma", familyName: "Watson"});

    instance1 == instance2; //TRUE
    instance1 === instance2; //TRUE


  • 也许不用编写自己的打字稿(预处理器),你可以通过使用TypeScript装饰器来实现你的目标。

    这个例子来自https://www.typescriptlang.org/docs/handbook/decorators.html:

    @sealed
    class Greeter {
        greeting: string;
        constructor(message: string) {
            this.greeting = message;
        }
        greet() {
            return "Hello, " + this.greeting;
        }
    }
    
    function sealed(constructor: Function) {
        Object.seal(constructor);
        Object.seal(constructor.prototype);
    }
    

    这是我迄今发现的。 目前没有简单的方法(2017年)。

    一种解决方案是创建一个使用Typescript API的自定义插件。 它还将在自己的沙箱中第二次运行服务,并与VS CodeAtomVS TS服务一起运行。 除此之外,每个IDE都需要将自己的插件创建为核心插件/服务之上的包装。

    通过这种方式,有些人已经制作了短信,例如vscode-ng-language-service和ng2linter。

    Microsoft拥有用于TypeScript扩展性的#6508票据,这将使请求的功能成为可能并且易于实现。


    对于那些使用C#F#编程的人来说,使用Roslyn的可能性可能会更好,而不是等待TypeScript扩展。 用C#编写的代码可以转换为TypeScript或JavaScript。 它还为各种检查和自定义修改打开了广泛的可能性。 当然,如果在.NETTypeScript/Javascript有相同的逻辑,它更接近DRY原则。 Bridge.NET不使用Roslyn,但有很好的实现。 用于.NET 4和Roslyn的Rosetta项目似乎是一个好的开始。


    没有任何编译器的魔力,这都是可能的。

    外部库

    declare function someGenericEqualsFn(a, b): boolean;
    declare function makeCached<TResult, TFunc extends (...args) => TResult>(funcToCache: TFunc): TFunc;
    declare function merge<T>(objectToUpdate: T, objectToMerge: Partial<T>);
    

    我们的库:

    interface DataRecord<TRecord> {
        equals<TRecord>(other: TRecord): boolean;
        update(update: Partial<TRecord>);
    }
    
    function createDataRecord<TRecord>(data: TRecord): Readonly<TRecord> & DataRecord<TRecord> {
        const result: TRecord & DataRecord<TRecord> = <any>{};
        Object.keys(data).forEach(() => {
    
        });
    
        result.equals = function (other: TRecord) {
            return someGenericEqualsFn(result, other);
        };
    
        result.update = function (partial: Partial<TRecord>) {
            merge(result, partial);
        };
    
        return result;
    }
    

    我们的测试:

    interface IPerson {
        givenName: string;
        familyName: string;
    }
    
    let instance = createDataRecord<IPerson>({givenName: "Emma", familyName: "Watson"});
    instance = createDataRecord<IPerson>({nonExistedProperty: "Emma"}); // compiler error
    instance.givenName = "John";             // compiler error
    instance.update({givenName: "Emma"});     //   works!
    instance.update({nonExistedProperty: "x"});      // compiler error
    
    const createDataRecordOrGetCached = makeCached(createDataRecord);
    let instance1 = createDataRecordOrGetCached({givenName: "Emma", familyName: "Watson"});
    let instance2 = createDataRecordOrGetCached({givenName: "Emma", familyName: "Watson"});
    
    instance1 == instance2; //TRUE
    instance1 === instance2; //TRUE
    
    链接地址: http://www.djcxy.com/p/38153.html

    上一篇: Typescript custom tool, transpiler, extension

    下一篇: Is it possible to overload deconstructors in C# 7.0?