import {expectTranslate} from './test_support'; describe('types', () => { it('supports qualified names', () => { expectTranslate('var x: foo.Bar;').to.equal(`@JS() external foo.Bar get x; @JS() external set x(foo.Bar v);`); }); it('supports null types', () => { expectTranslate('export function attr(name: string, value: null);').to.equal(`@JS() external attr(String name, Null value);`); expectTranslate(`export function style(name: string, priority?: 'regular' | 'important');`) .to.equal(`@JS() external style(String name, [String /*'regular'|'important'*/ priority]);`); expectTranslate(`export function style(name: string, priority?: null | 'important');`) .to.equal(`@JS() external style(String name, [String /*Null|'important'*/ priority]);`); expectTranslate('var foo: null;').to.equal(`@JS() external Null get foo; @JS() external set foo(Null v);`); }); it('supports this return type', () => { expectTranslate('export interface Foo { bar() : this; }').to.equal(`@anonymous @JS() abstract class Foo { external Foo bar(); }`); }); it('supports true and false return types', () => { expectTranslate('export function f(): true;').to.equal(`@JS() external bool /*true*/ f();`); expectTranslate('export function g(): false;').to.equal(`@JS() external bool /*false*/ g();`); }); it('comment type literals', () => { expectTranslate('var x: {x: string, y: number};').to.equal(`@JS() external dynamic /*{x: string, y: number}*/ get x; @JS() external set x(dynamic /*{x: string, y: number}*/ v);`); }); it('do not translates string index signatures to dartisms', () => { // We wish these could be just Map but sadly can't support // that yet. expectTranslate('var x: {[k: string]: any[]};').to.equal(`@JS() external dynamic /*JSMap of >*/ get x; @JS() external set x(dynamic /*JSMap of >*/ v);`); expectTranslate('var x: {[k: number]: number};').to.equal(`@JS() external dynamic /*JSMap of */ get x; @JS() external set x(dynamic /*JSMap of */ v);`); }); it('drops type literals with index signatures and other properties', () => { expectTranslate('var x: {a: number, [k: string]: number};').to.equal(`@JS() external dynamic /*{a: number, [k: string]: number}*/ get x; @JS() external set x(dynamic /*{a: number, [k: string]: number}*/ v);`); }); it('should support union types', () => { expectTranslate('function foo() : number | number[];').to.equal(`@JS() external dynamic /*num|List*/ foo();`); expectTranslate('var x: number|Array;').to.equal(`@JS() external dynamic /*num|List*/ get x; @JS() external set x(dynamic /*num|List*/ v);`); expectTranslate('function x(): number|Array<{[k: string]: any}> {};').to.equal(`@JS() external dynamic /*num|List>*/ x();`); }); it('should support intersection types', () => { expectTranslate(` interface Foo { a: number, b: string } interface Bar { b: string } function foo() : Foo & Bar; `).to.equal(`@anonymous @JS() abstract class Foo { external num get a; external set a(num v); external String get b; external set b(String v); external factory Foo({num a, String b}); } @anonymous @JS() abstract class Bar { external String get b; external set b(String v); external factory Bar({String b}); } @JS() external Foo /*Foo&Bar*/ foo();`); }); it('should support parenthesized types', () => { expectTranslate('function foo() : (number | number[]);').to.equal(`@JS() external dynamic /*num|List*/ foo();`); expectTranslate('var x: (number|Array);').to.equal(`@JS() external dynamic /*num|List*/ get x; @JS() external set x(dynamic /*num|List*/ v);`); expectTranslate('function x(): number|(Array<{[k: string]: any}>) {};').to.equal(`@JS() external dynamic /*num|List>*/ x();`); }); it('should support array types', () => { expectTranslate('var x: string[] = [];').to.equal(`@JS() external List get x; @JS() external set x(List v);`); }); it('should support function types', () => { expectTranslate('var x: (a: string) => string;').to.equal(`@JS() external String Function(String) get x; @JS() external set x(String Function(String) v);`); expectTranslate('declare var a: Function').to.equal(`@JS() external Function get a; @JS() external set a(Function v);`); }); describe('TypeScript utility types and other mapped types', () => { it('emits X in place of Partial since all Dart types are currently nullable', () => { expectTranslate('interface X { a: number; } declare const x: Partial;') .to.equal(`@anonymous @JS() abstract class X { external num get a; external set a(num v); external factory X({num a}); } @JS() external X /*Partial*/ get x;`); }); it('treats other mapped types as dynamic', () => { expectTranslate(`interface Todo { task: string; } type ReadonlyTodo = { readonly[P in keyof Todo]: Todo[P]; } declare const todo: ReadonlyTodo;`) .to.equal(`@anonymous @JS() abstract class Todo { external String get task; external set task(String v); external factory Todo({String task}); } /* Warning: Mapped types are not supported in Dart. Uses of this type will be replaced by dynamic. type ReadonlyTodo = { readonly[P in keyof Todo]: Todo[P]; } */ @JS() external dynamic /*ReadonlyTodo*/ get todo;`); }); }); it('should support conditional types', () => { expectTranslate(` type TypeName = T extends string ? "string" : T extends string ? "string" : T extends number ? "number" : T extends boolean ? "boolean" : T extends undefined ? "undefined" : T extends Function ? "function" : "object"; declare var x: TypeName; declare var y: TypeName; declare var z: TypeName;`) .to.equal( `/*Warning: Conditional types are not supported in Dart. Uses of this type will be replaced by dynamic. type TypeName = T extends string ? "string" : T extends string ? "string" : T extends number ? "number" : T extends boolean ? "boolean" : T extends undefined ? "undefined" : T extends Function ? "function" : "object"; */ @JS() external dynamic /*TypeName*/ get x; @JS() external set x(dynamic /*TypeName*/ v); @JS() external dynamic /*TypeName*/ get y; @JS() external set y(dynamic /*TypeName*/ v); @JS() external dynamic /*TypeName*/ get z; @JS() external set z(dynamic /*TypeName*/ v);`); }); }); describe('type arguments', () => { it('should support declaration', () => { expectTranslate('class X { a: A; }').to.equal(`@JS() class X { // @Ignore X.fakeConstructor$(); external A get a; external set a(A v); }`); }); it('should support nested extends', () => { expectTranslate('class X> { }').to.equal(`@JS() class X> { // @Ignore X.fakeConstructor$(); }`); }); it('should multiple extends', () => { expectTranslate('class X { }').to.equal(`@JS() class X { // @Ignore X.fakeConstructor$(); }`); }); it('should support use', () => { expectTranslate('class X extends Y { }').to.equal(`@JS() class X extends Y { // @Ignore X.fakeConstructor$() : super.fakeConstructor$(); }`); }); it('should handle and generic arguments', () => { expectTranslate('var x: X;').to.equal(`@JS() external X get x; @JS() external set x(X v);`); expectTranslate('class X extends Y { }').to.equal(`@JS() class X extends Y { // @Ignore X.fakeConstructor$() : super.fakeConstructor$(); }`); expectTranslate('class X extends Y { }').to.equal(`@JS() class X extends Y { // @Ignore X.fakeConstructor$() : super.fakeConstructor$(); }`); expectTranslate('var z : Y;').to.equal(`@JS() external Y get z; @JS() external set z(Y v);`); expectTranslate('var z : Y;').to.equal(`@JS() external Y get z; @JS() external set z(Y v);`); }); it('should create class for type alias literals', () => { expectTranslate(`/** * Event Parameters. */ export type EventParameters = { bubbles: boolean; /** * Is cancelable. */ cancelable: boolean; }; export function dispatch(parameters: EventParameters): void;`) .to.equal(`/// Event Parameters. @anonymous @JS() abstract class EventParameters { external bool get bubbles; external set bubbles(bool v); /// Is cancelable. external bool get cancelable; external set cancelable(bool v); external factory EventParameters({bool bubbles, bool cancelable}); } @JS() external void dispatch(EventParameters parameters);`); expectTranslate(`/** * Event Parameters. */ export type EventParameters = { bubbles: T; /** * Is cancelable. */ cancelable: T; }; export function dispatch(parameters: EventParameters): void;`) .to.equal(`/// Event Parameters. @anonymous @JS() abstract class EventParameters { external T get bubbles; external set bubbles(T v); /// Is cancelable. external T get cancelable; external set cancelable(T v); external factory EventParameters({T bubbles, T cancelable}); } @JS() external void dispatch(EventParameters parameters);`); }); it('should create typedef for type alias function literals', () => { expectTranslate(` export type ValueFn = (this: T, a: A, b: B) => A; export type SimpleValueFn = (a: A, b: B) => A; export function dispatch(callback: ValueFn): void; export function dispatchSimple(callback: SimpleValueFn): void;`) .to.equal(`import "dart:html" show Element; typedef A ValueFn(/*T this*/ A a, B b); typedef A SimpleValueFn(A a, B b); @JS() external void dispatch(ValueFn callback); @JS() external void dispatchSimple(SimpleValueFn callback);`); }); it('should handle generic parameters on non dart compatible type aliases', () => { expectTranslate(` export type Triangle = [G, G, G]; export type ListOfLists = [G[]]; export function triangles(): Triangle[]; `).to.equal(`/*export type Triangle = [G, G, G];*/ /*export type ListOfLists = [G[]];*/ @JS() external List /*Tuple of */ > triangles/**/();`); }); it('supports the keyof operator and the indexed access operator', () => { expectTranslate(`export interface A { a: number; } export function f(first: K, second: A[K]): boolean;`) .to.equal(`@anonymous @JS() abstract class A { external num get a; external set a(num v); external factory A({num a}); } @JS() external bool f/**/( dynamic /*K*/ first, dynamic /*A[K]*/ second);`); }); });