Typescript custom tool, transpiler, extension
I would like to alter/check .ts
files before tsc
will start its transpilation process, similar to what Roslyn provides for C#.
This is useful for static type checking. For example, proper and short Immutable Record
implementation requires code altering/static check at compile time, as it can be done with Flow:
@Record(Person)
interface IPerson {
givenName: string;
familyName: string;
}
and then custom tsc transpiler could modify code to:
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
});
}
}
It would be nice to see custom compilation errors immediately as it is done now with Visual Studio and Visual Studio Code, which run special tsc watch
, not as part of webpack bundling or custom gulp task. There is an API for Typescript, but how to make it work seamless with tsc
in VS/VS Code/Atom
?
Update with examples
The goal is just to write
@Record(Person)
interface IPerson {
givenName: string;
familyName: string;
}
The class Person
will be auto generated based on the interface IPerson
as shown earlier.
It will be possible to instantiate the object:
let instance = new Person({givenName: "Emma", familyName: "Watson"});
Any incorrect property will raise a compilation error:
let instance = new Person({nonExistedProperty: "Emma"}); //error
Error: Property 'nonExistedProperty' does not exist in class Person constructor;
Error: Property 'givenName' is required in class Person constructor;
Error: Property 'familyName' is required in class Person constructor;
Existing object should be able to be partially updated
let instance = new Person({givenName: "Emma", familyName: "Watson"});
instance.Update({givenName: "Luise"});
instance.givenName === "Luise"; //TRUE;
instance.familyName === "Watson"; //TRUE;
All properties are readonly
let instance = new Person({givenName: "Emma", familyName: "Watson"});
instance.givenName = "John"; //error
Error: Property 'givenName' is readonly;
Equals
method is autogenerated. It can be based on hash or anything else, but should work fast and provide a deep check.
let instance1 = new Person({givenName: "Emma", familyName: "Watson"});
let instance2 = new Person({givenName: "Emma", familyName: "Watson"});
instance1.Equals(instance2); //TRUE
It might also have a place for controlling created instances and if a record with the same parameters exists in internal dictionary then it just returns the reference to this object:
let instance1 = new Person({givenName: "Emma", familyName: "Watson"});
let instance2 = new Person({givenName: "Emma", familyName: "Watson"});
instance1 == instance2; //TRUE
instance1 === instance2; //TRUE
Maybe instead of writing your own typescript (pre) processor, you could achieve your goals by using TypeScript decorators.
This example is from 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);
}
This is what I've found so far. There is no simple way to do it at this moment (2017).
One solution is to create a custom plugin that will use Typescript API. It will also run the services second time in its own sandbox alongside with TS services in VS Code
, Atom
or VS
. In addition to this each IDE will require its own plugin to be created as a wrapper on top of your core plugin/service.
In this way some people already made linters, for example, vscode-ng-language-service and ng2linter.
Microsoft has #6508 ticket for TypeScript extensibility, which will make the requested features possible and simple to implement.
For those who program in C#
and F#
it might be better to use the possibilities of Roslyn
instead of waiting TypeScript extensions. A code written in C# can be transpiled to TypeScript or JavaScript. It also opens the wide possibilities for all kind of checking and custom modifications. And of course it is more close to DRY principle if there is the same logic in .NET
and TypeScript/Javascript
. Bridge.NET does not use Roslyn, but has a good implementation. Rosetta project for .NET 4 and Roslyn seems to be a good start.
This is all possible without any compiler magic.
external libs
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>);
our lib:
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;
}
our test:
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/38154.html
上一篇: 绘制训练,验证和测试集精度