From 412586d8bf8526aefe96369ff927a437b194b135 Mon Sep 17 00:00:00 2001 From: Martin Probst Date: Thu, 10 Nov 2016 10:00:57 -0800 Subject: [PATCH 1/4] Enable strict null checks in the tsickle test suite. All tsickle users (AFAIK) use strict null checks, and enabling these is required to properly test our nullable types conversion. --- test/test_support.ts | 1 + test_files/basic.untyped/basic.untyped.js | 2 +- test_files/basic.untyped/basic.untyped.ts | 4 +- .../basic.untyped/basic.untyped.tsickle.ts | 4 +- test_files/declare_class_overloads/externs.js | 6 +-- test_files/decorator/decorator.tsickle.ts | 4 +- test_files/enum/enum.js | 4 +- test_files/enum/enum.ts | 4 +- test_files/enum/enum.tsickle.ts | 6 ++- test_files/export/export_helper.js | 1 - test_files/export/export_helper.ts | 2 +- test_files/export/export_helper.tsickle.ts | 2 +- test_files/interface/interface.js | 2 +- test_files/interface/interface.tsickle.ts | 2 +- test_files/jsdoc_types.untyped/jsdoc_types.js | 16 ++++---- test_files/jsdoc_types.untyped/jsdoc_types.ts | 16 ++++---- .../jsdoc_types.tsickle.ts | 16 ++++---- test_files/jsdoc_types/jsdoc_types.js | 16 ++++---- test_files/jsdoc_types/jsdoc_types.ts | 16 ++++---- test_files/jsdoc_types/jsdoc_types.tsickle.ts | 16 ++++---- test_files/nullable/nullable.js | 26 ++++++++++++ test_files/nullable/nullable.tsickle.ts | 41 +++++++++++++++++++ test_files/optional/optional.js | 4 +- test_files/optional/optional.tsickle.ts | 4 +- test_files/type/type.js | 16 ++++---- test_files/type/type.ts | 12 +++--- test_files/type/type.tsickle.ts | 16 ++++---- test_files/type_and_value/type_and_value.js | 6 +-- test_files/type_and_value/type_and_value.ts | 6 +-- .../type_and_value/type_and_value.tsickle.ts | 6 +-- 30 files changed, 173 insertions(+), 104 deletions(-) create mode 100644 test_files/nullable/nullable.js create mode 100644 test_files/nullable/nullable.tsickle.ts diff --git a/test/test_support.ts b/test/test_support.ts index b475600f2..9c25d0542 100644 --- a/test/test_support.ts +++ b/test/test_support.ts @@ -27,6 +27,7 @@ const compilerOptions: ts.CompilerOptions = { // Flags below are needed to make sure source paths are correctly set on write calls. rootDir: path.resolve(process.cwd()), outDir: '.', + strictNullChecks: true, }; const {cachedLibPath, cachedLib} = (function() { diff --git a/test_files/basic.untyped/basic.untyped.js b/test_files/basic.untyped/basic.untyped.js index e00bc011f..1ebe8f9f1 100644 --- a/test_files/basic.untyped/basic.untyped.js +++ b/test_files/basic.untyped/basic.untyped.js @@ -24,7 +24,7 @@ function Foo_tsickle_Closure_declarations() { // regardless of untyped. (function () { // With a type annotation: - let { a, b } = { a: null, b: null }; + let { a, b } = { a: '', b: 0 }; })(); (function () { // Without a type annotation: diff --git a/test_files/basic.untyped/basic.untyped.ts b/test_files/basic.untyped/basic.untyped.ts index b8950245e..b1d76c2d4 100644 --- a/test_files/basic.untyped/basic.untyped.ts +++ b/test_files/basic.untyped/basic.untyped.ts @@ -16,9 +16,9 @@ class Foo { // regardless of untyped. (function() { // With a type annotation: - let {a, b}: {a:string, b:number} = {a:null, b:null}; + let {a, b}: {a: string, b: number} = {a: '', b: 0}; })(); (function() { // Without a type annotation: - let {a, b} = {a:null, b:null}; + let {a, b} = {a: null, b: null}; })(); diff --git a/test_files/basic.untyped/basic.untyped.tsickle.ts b/test_files/basic.untyped/basic.untyped.tsickle.ts index df6b06353..9d9643ecd 100644 --- a/test_files/basic.untyped/basic.untyped.tsickle.ts +++ b/test_files/basic.untyped/basic.untyped.tsickle.ts @@ -29,9 +29,9 @@ Foo.prototype.ctorArg; // regardless of untyped. (function() { // With a type annotation: - let {a, b}: {a:string, b:number} = {a:null, b:null}; + let {a, b}: {a: string, b: number} = {a: '', b: 0}; })(); (function() { // Without a type annotation: - let {a, b} = {a:null, b:null}; + let {a, b} = {a: null, b: null}; })(); diff --git a/test_files/declare_class_overloads/externs.js b/test_files/declare_class_overloads/externs.js index 789e4e578..278e9df52 100644 --- a/test_files/declare_class_overloads/externs.js +++ b/test_files/declare_class_overloads/externs.js @@ -31,9 +31,9 @@ function MultipleConstructorsNamesAndTypes(a_or_b) {} * @param {number} a * @param {number} b * @param {number} c - * @param {string|Array} normal_or_vertexNormals - * @param {boolean|Array} color_or_vertexColors - * @param {number} materialIndex + * @param {(undefined|string)|(undefined|Array)} normal_or_vertexNormals + * @param {(undefined|boolean)|(undefined|Array)} color_or_vertexColors + * @param {(undefined|number)} materialIndex */ function MultipleConstructorsComplexMatrix(a, b, c, normal_or_vertexNormals, color_or_vertexColors, materialIndex) {} diff --git a/test_files/decorator/decorator.tsickle.ts b/test_files/decorator/decorator.tsickle.ts index 947bbcb1a..851e3f72d 100644 --- a/test_files/decorator/decorator.tsickle.ts +++ b/test_files/decorator/decorator.tsickle.ts @@ -42,7 +42,7 @@ function DecoratorTest_tsickle_Closure_declarations() { /** @type {Array} */ DecoratorTest.decorators; /** @nocollapse - @type {Array<{type: ?, decorators: (Array|undefined)}>} */ + @type {Array<(null|{type: ?, decorators: ((undefined|Array)|undefined)})>} */ DecoratorTest.ctorParameters; /** @type {Object>} */ DecoratorTest.propDecorators; @@ -67,7 +67,7 @@ DecoratedClass.prototype.z; function DecoratorInvocation() {} /** @type {Function} */ DecoratorInvocation.prototype.type; - /** @type {Array} */ + /** @type {(undefined|Array)} */ DecoratorInvocation.prototype.args; diff --git a/test_files/enum/enum.js b/test_files/enum/enum.js index ef0117b67..c2f46eeae 100644 --- a/test_files/enum/enum.js +++ b/test_files/enum/enum.js @@ -13,8 +13,8 @@ EnumTest1[EnumTest1.PI] = "PI"; // index into the enum with all the various ways allowed of enums. let /** @type {number} */ enumTestValue = EnumTest1.XYZ; let /** @type {number} */ enumTestValue2 = EnumTest1['XYZ']; -let /** @type {string} */ enumNumIndex = EnumTest1[(null)]; -let /** @type {number} */ enumStrIndex = EnumTest1[(null)]; +let /** @type {string} */ enumNumIndex = EnumTest1[((null))]; +let /** @type {number} */ enumStrIndex = EnumTest1[((null))]; /** * @param {number} val * @return {void} diff --git a/test_files/enum/enum.ts b/test_files/enum/enum.ts index 8b269eda3..82a87233e 100644 --- a/test_files/enum/enum.ts +++ b/test_files/enum/enum.ts @@ -7,8 +7,8 @@ enum EnumTest1 {XYZ, PI = 3.14159} // index into the enum with all the various ways allowed of enums. let enumTestValue: EnumTest1 = EnumTest1.XYZ; let enumTestValue2: EnumTest1 = EnumTest1['XYZ']; -let enumNumIndex: string = EnumTest1[null as number]; -let enumStrIndex: number = EnumTest1[null as string]; +let enumNumIndex: string = EnumTest1[null as any as number]; +let enumStrIndex: number = EnumTest1[null as any as string]; function enumTestFunction(val: EnumTest1) {} enumTestFunction(enumTestValue); diff --git a/test_files/enum/enum.tsickle.ts b/test_files/enum/enum.tsickle.ts index 29a90db24..7e3c665db 100644 --- a/test_files/enum/enum.tsickle.ts +++ b/test_files/enum/enum.tsickle.ts @@ -1,3 +1,5 @@ +Warning at test_files/enum/enum.ts:2:7: unhandled type {type flags:0x2000 } +==== // Line with a missing semicolon should not break the following enum. const /** @type {Array} */ EnumTestMissingSemi = [] type EnumTest1 = number; @@ -15,8 +17,8 @@ EnumTest1[EnumTest1.PI] = "PI"; // index into the enum with all the various ways allowed of enums. let /** @type {number} */ enumTestValue: EnumTest1 = EnumTest1.XYZ; let /** @type {number} */ enumTestValue2: EnumTest1 = EnumTest1['XYZ']; -let /** @type {string} */ enumNumIndex: string = EnumTest1[ /** @type {number} */((null as number))]; -let /** @type {number} */ enumStrIndex: number = EnumTest1[ /** @type {string} */((null as string))]; +let /** @type {string} */ enumNumIndex: string = EnumTest1[ /** @type {number} */(( /** @type {?} */((null as any)) as number))]; +let /** @type {number} */ enumStrIndex: number = EnumTest1[ /** @type {string} */(( /** @type {?} */((null as any)) as string))]; /** * @param {number} val * @return {void} diff --git a/test_files/export/export_helper.js b/test_files/export/export_helper.js index c7b73a367..eccc7996d 100644 --- a/test_files/export/export_helper.js +++ b/test_files/export/export_helper.js @@ -10,5 +10,4 @@ function Bar() { } exports.Bar = Bar; /** @type {number} */ Bar.prototype.barField; -exports.export3 = null; exports.export5 = 3; diff --git a/test_files/export/export_helper.ts b/test_files/export/export_helper.ts index 41a1ec3b7..8ee068d8a 100644 --- a/test_files/export/export_helper.ts +++ b/test_files/export/export_helper.ts @@ -5,6 +5,6 @@ export let export1 = 3; export let export2 = 3; export interface Bar { barField: number; } -export var export3: Bar = null; +export var export3: Bar; export let export5 = 3; diff --git a/test_files/export/export_helper.tsickle.ts b/test_files/export/export_helper.tsickle.ts index a9f808808..47f399d74 100644 --- a/test_files/export/export_helper.tsickle.ts +++ b/test_files/export/export_helper.tsickle.ts @@ -10,6 +10,6 @@ Bar.prototype.barField; export interface Bar { barField: number; } -export var /** @type {Bar} */ export3: Bar = null; +export var /** @type {Bar} */ export3: Bar; export let /** @type {number} */ export5 = 3; diff --git a/test_files/interface/interface.js b/test_files/interface/interface.js index 66fefd4d0..63be54ebe 100644 --- a/test_files/interface/interface.js +++ b/test_files/interface/interface.js @@ -25,5 +25,5 @@ TrickyInterface.prototype.foo; (x: number): __ yuck __ number; */ -/** @type {string} */ +/** @type {(undefined|string)} */ TrickyInterface.prototype.foobar; diff --git a/test_files/interface/interface.tsickle.ts b/test_files/interface/interface.tsickle.ts index 9d81a07d5..6200416af 100644 --- a/test_files/interface/interface.tsickle.ts +++ b/test_files/interface/interface.tsickle.ts @@ -31,7 +31,7 @@ TrickyInterface.prototype.foo; (x: number): __ yuck __ number; */ - /** @type {string} */ + /** @type {(undefined|string)} */ TrickyInterface.prototype.foobar; diff --git a/test_files/jsdoc_types.untyped/jsdoc_types.js b/test_files/jsdoc_types.untyped/jsdoc_types.js index a45f57bea..43422475f 100644 --- a/test_files/jsdoc_types.untyped/jsdoc_types.js +++ b/test_files/jsdoc_types.untyped/jsdoc_types.js @@ -10,20 +10,20 @@ var module2_3 = module2_1; var default_1 = goog.require('test_files.jsdoc_types.untyped.default'); // Check that imported types get the proper names in JSDoc. let /** @type {?} */ useNamespacedClass = new module1.Class(); -let /** @type {?} */ useNamespacedClassAsType = null; -let /** @type {?} */ useNamespacedType = null; +let /** @type {?} */ useNamespacedClassAsType; +let /** @type {?} */ useNamespacedType; // Should be references to the symbols in module2, perhaps via locals. let /** @type {?} */ useLocalClass = new module2_1.ClassOne(); let /** @type {?} */ useLocalClassRenamed = new module2_2.ClassOne(); let /** @type {?} */ useLocalClassRenamedTwo = new module2_3.ClassTwo(); -let /** @type {?} */ useLocalClassAsTypeRenamed = null; -let /** @type {?} */ useLocalInterface = null; -let /** @type {?} */ useClassWithParams = null; +let /** @type {?} */ useLocalClassAsTypeRenamed; +let /** @type {?} */ useLocalInterface; +let /** @type {?} */ useClassWithParams; // This is purely a value; it doesn't need renaming. let /** @type {?} */ useLocalValue = module2_1.value; // Check a default import. let /** @type {?} */ useDefaultClass = new default_1.default(); -let /** @type {?} */ useDefaultClassAsType = null; +let /** @type {?} */ useDefaultClassAsType; // NeverTyped should be {?}, even in typed mode. -let /** @type {?} */ useNeverTyped = null; -let /** @type {?} */ useNeverTyped2 = null; +let /** @type {?} */ useNeverTyped; +let /** @type {?} */ useNeverTyped2; diff --git a/test_files/jsdoc_types.untyped/jsdoc_types.ts b/test_files/jsdoc_types.untyped/jsdoc_types.ts index 7bd22c5be..ca9c63fe1 100644 --- a/test_files/jsdoc_types.untyped/jsdoc_types.ts +++ b/test_files/jsdoc_types.untyped/jsdoc_types.ts @@ -14,24 +14,24 @@ import {NeverTyped} from './nevertyped'; // Check that imported types get the proper names in JSDoc. let useNamespacedClass = new module1.Class(); -let useNamespacedClassAsType: module1.Class = null; -let useNamespacedType: module1.Interface = null; +let useNamespacedClassAsType: module1.Class; +let useNamespacedType: module1.Interface; // Should be references to the symbols in module2, perhaps via locals. let useLocalClass = new ClassOne(); let useLocalClassRenamed = new RenamedClassOne(); let useLocalClassRenamedTwo = new RenamedClassTwo(); -let useLocalClassAsTypeRenamed: RenamedClassOne = null; -let useLocalInterface: Interface = null; -let useClassWithParams: ClassWithParams = null; +let useLocalClassAsTypeRenamed: RenamedClassOne; +let useLocalInterface: Interface; +let useClassWithParams: ClassWithParams; // This is purely a value; it doesn't need renaming. let useLocalValue = value; // Check a default import. let useDefaultClass = new DefaultClass(); -let useDefaultClassAsType: DefaultClass = null; +let useDefaultClassAsType: DefaultClass; // NeverTyped should be {?}, even in typed mode. -let useNeverTyped: NeverTyped = null; -let useNeverTyped2: string|NeverTyped = null; +let useNeverTyped: NeverTyped; +let useNeverTyped2: string|NeverTyped; diff --git a/test_files/jsdoc_types.untyped/jsdoc_types.tsickle.ts b/test_files/jsdoc_types.untyped/jsdoc_types.tsickle.ts index 0019905d7..ce7bd4ffc 100644 --- a/test_files/jsdoc_types.untyped/jsdoc_types.tsickle.ts +++ b/test_files/jsdoc_types.untyped/jsdoc_types.tsickle.ts @@ -14,24 +14,24 @@ import {NeverTyped} from './nevertyped'; // Check that imported types get the proper names in JSDoc. let /** @type {?} */ useNamespacedClass = new module1.Class(); -let /** @type {?} */ useNamespacedClassAsType: module1.Class = null; -let /** @type {?} */ useNamespacedType: module1.Interface = null; +let /** @type {?} */ useNamespacedClassAsType: module1.Class; +let /** @type {?} */ useNamespacedType: module1.Interface; // Should be references to the symbols in module2, perhaps via locals. let /** @type {?} */ useLocalClass = new ClassOne(); let /** @type {?} */ useLocalClassRenamed = new RenamedClassOne(); let /** @type {?} */ useLocalClassRenamedTwo = new RenamedClassTwo(); -let /** @type {?} */ useLocalClassAsTypeRenamed: RenamedClassOne = null; -let /** @type {?} */ useLocalInterface: Interface = null; -let /** @type {?} */ useClassWithParams: ClassWithParams = null; +let /** @type {?} */ useLocalClassAsTypeRenamed: RenamedClassOne; +let /** @type {?} */ useLocalInterface: Interface; +let /** @type {?} */ useClassWithParams: ClassWithParams; // This is purely a value; it doesn't need renaming. let /** @type {?} */ useLocalValue = value; // Check a default import. let /** @type {?} */ useDefaultClass = new DefaultClass(); -let /** @type {?} */ useDefaultClassAsType: DefaultClass = null; +let /** @type {?} */ useDefaultClassAsType: DefaultClass; // NeverTyped should be {?}, even in typed mode. -let /** @type {?} */ useNeverTyped: NeverTyped = null; -let /** @type {?} */ useNeverTyped2: string|NeverTyped = null; +let /** @type {?} */ useNeverTyped: NeverTyped; +let /** @type {?} */ useNeverTyped2: string|NeverTyped; diff --git a/test_files/jsdoc_types/jsdoc_types.js b/test_files/jsdoc_types/jsdoc_types.js index 2f2179fbf..995f176c4 100644 --- a/test_files/jsdoc_types/jsdoc_types.js +++ b/test_files/jsdoc_types/jsdoc_types.js @@ -17,20 +17,20 @@ var nevertyped_1 = goog.require('test_files.jsdoc_types.nevertyped'); const NeverTyped = nevertyped_1.NeverTyped; /* local alias for Closure JSDoc */ // Check that imported types get the proper names in JSDoc. let /** @type {module1.Class} */ useNamespacedClass = new module1.Class(); -let /** @type {module1.Class} */ useNamespacedClassAsType = null; -let /** @type {module1.Interface} */ useNamespacedType = null; +let /** @type {module1.Class} */ useNamespacedClassAsType; +let /** @type {module1.Interface} */ useNamespacedType; // Should be references to the symbols in module2, perhaps via locals. let /** @type {ClassOne} */ useLocalClass = new module2_1.ClassOne(); let /** @type {ClassOne} */ useLocalClassRenamed = new module2_1.ClassOne(); let /** @type {RenamedClassTwo} */ useLocalClassRenamedTwo = new module2_1.ClassTwo(); -let /** @type {ClassOne} */ useLocalClassAsTypeRenamed = null; -let /** @type {Interface} */ useLocalInterface = null; -let /** @type {ClassWithParams} */ useClassWithParams = null; +let /** @type {ClassOne} */ useLocalClassAsTypeRenamed; +let /** @type {Interface} */ useLocalInterface; +let /** @type {ClassWithParams} */ useClassWithParams; // This is purely a value; it doesn't need renaming. let /** @type {number} */ useLocalValue = module2_1.value; // Check a default import. let /** @type {DefaultClass} */ useDefaultClass = new default_1.default(); -let /** @type {DefaultClass} */ useDefaultClassAsType = null; +let /** @type {DefaultClass} */ useDefaultClassAsType; // NeverTyped should be {?}, even in typed mode. -let /** @type {?} */ useNeverTyped = null; -let /** @type {(string|?)} */ useNeverTyped2 = null; +let /** @type {?} */ useNeverTyped; +let /** @type {(string|?)} */ useNeverTyped2; diff --git a/test_files/jsdoc_types/jsdoc_types.ts b/test_files/jsdoc_types/jsdoc_types.ts index 17da28d9b..3780ae38c 100644 --- a/test_files/jsdoc_types/jsdoc_types.ts +++ b/test_files/jsdoc_types/jsdoc_types.ts @@ -10,24 +10,24 @@ import {NeverTyped} from './nevertyped'; // Check that imported types get the proper names in JSDoc. let useNamespacedClass = new module1.Class(); -let useNamespacedClassAsType: module1.Class = null; -let useNamespacedType: module1.Interface = null; +let useNamespacedClassAsType: module1.Class; +let useNamespacedType: module1.Interface; // Should be references to the symbols in module2, perhaps via locals. let useLocalClass = new ClassOne(); let useLocalClassRenamed = new RenamedClassOne(); let useLocalClassRenamedTwo = new RenamedClassTwo(); -let useLocalClassAsTypeRenamed: RenamedClassOne = null; -let useLocalInterface: Interface = null; -let useClassWithParams: ClassWithParams = null; +let useLocalClassAsTypeRenamed: RenamedClassOne; +let useLocalInterface: Interface; +let useClassWithParams: ClassWithParams; // This is purely a value; it doesn't need renaming. let useLocalValue = value; // Check a default import. let useDefaultClass = new DefaultClass(); -let useDefaultClassAsType: DefaultClass = null; +let useDefaultClassAsType: DefaultClass; // NeverTyped should be {?}, even in typed mode. -let useNeverTyped: NeverTyped = null; -let useNeverTyped2: string|NeverTyped = null; +let useNeverTyped: NeverTyped; +let useNeverTyped2: string|NeverTyped; diff --git a/test_files/jsdoc_types/jsdoc_types.tsickle.ts b/test_files/jsdoc_types/jsdoc_types.tsickle.ts index fc789ad06..4a7211ef3 100644 --- a/test_files/jsdoc_types/jsdoc_types.tsickle.ts +++ b/test_files/jsdoc_types/jsdoc_types.tsickle.ts @@ -18,24 +18,24 @@ const NeverTyped: NeverTypeCheckMe = NeverTyped; /* local alias for Closure JSD // Check that imported types get the proper names in JSDoc. let /** @type {module1.Class} */ useNamespacedClass = new module1.Class(); -let /** @type {module1.Class} */ useNamespacedClassAsType: module1.Class = null; -let /** @type {module1.Interface} */ useNamespacedType: module1.Interface = null; +let /** @type {module1.Class} */ useNamespacedClassAsType: module1.Class; +let /** @type {module1.Interface} */ useNamespacedType: module1.Interface; // Should be references to the symbols in module2, perhaps via locals. let /** @type {ClassOne} */ useLocalClass = new ClassOne(); let /** @type {ClassOne} */ useLocalClassRenamed = new RenamedClassOne(); let /** @type {RenamedClassTwo} */ useLocalClassRenamedTwo = new RenamedClassTwo(); -let /** @type {ClassOne} */ useLocalClassAsTypeRenamed: RenamedClassOne = null; -let /** @type {Interface} */ useLocalInterface: Interface = null; -let /** @type {ClassWithParams} */ useClassWithParams: ClassWithParams = null; +let /** @type {ClassOne} */ useLocalClassAsTypeRenamed: RenamedClassOne; +let /** @type {Interface} */ useLocalInterface: Interface; +let /** @type {ClassWithParams} */ useClassWithParams: ClassWithParams; // This is purely a value; it doesn't need renaming. let /** @type {number} */ useLocalValue = value; // Check a default import. let /** @type {DefaultClass} */ useDefaultClass = new DefaultClass(); -let /** @type {DefaultClass} */ useDefaultClassAsType: DefaultClass = null; +let /** @type {DefaultClass} */ useDefaultClassAsType: DefaultClass; // NeverTyped should be {?}, even in typed mode. -let /** @type {?} */ useNeverTyped: NeverTyped = null; -let /** @type {(string|?)} */ useNeverTyped2: string|NeverTyped = null; +let /** @type {?} */ useNeverTyped: NeverTyped; +let /** @type {(string|?)} */ useNeverTyped2: string|NeverTyped; diff --git a/test_files/nullable/nullable.js b/test_files/nullable/nullable.js new file mode 100644 index 000000000..2e1c2a859 --- /dev/null +++ b/test_files/nullable/nullable.js @@ -0,0 +1,26 @@ +goog.module('test_files.nullable.nullable');var module = module || {id: 'test_files/nullable/nullable.js'};class Primitives { +} +// tsickle -> Closure type declarations +/** @type {(null|string)} */ +Primitives.prototype.nullable; +/** @type {(undefined|number)} */ +Primitives.prototype.undefinable; +/** @type {(undefined|null|string)} */ +Primitives.prototype.nullableUndefinable; +/** @type {(undefined|string)} */ +Primitives.prototype.optional; +class NonPrimitive { +} +class NonPrimitives { +} +// tsickle -> Closure type declarations +/** @type {NonPrimitive} */ +NonPrimitives.prototype.nonNull; +/** @type {(null|NonPrimitive)} */ +NonPrimitives.prototype.nullable; +/** @type {(undefined|NonPrimitive)} */ +NonPrimitives.prototype.undefinable; +/** @type {(undefined|null|NonPrimitive)} */ +NonPrimitives.prototype.nullableUndefinable; +/** @type {(undefined|NonPrimitive)} */ +NonPrimitives.prototype.optional; diff --git a/test_files/nullable/nullable.tsickle.ts b/test_files/nullable/nullable.tsickle.ts new file mode 100644 index 000000000..6ebee6f75 --- /dev/null +++ b/test_files/nullable/nullable.tsickle.ts @@ -0,0 +1,41 @@ + +class Primitives { + nullable: string|null; + undefinable: number|undefined; + nullableUndefinable: string|null|undefined; + optional?: string; +} + +// tsickle -> Closure type declarations + /** @type {(null|string)} */ +Primitives.prototype.nullable; + /** @type {(undefined|number)} */ +Primitives.prototype.undefinable; + /** @type {(undefined|null|string)} */ +Primitives.prototype.nullableUndefinable; + /** @type {(undefined|string)} */ +Primitives.prototype.optional; + + +class NonPrimitive {} + +class NonPrimitives { + nonNull: NonPrimitive; + nullable: NonPrimitive|null; + undefinable: NonPrimitive|undefined; + nullableUndefinable: NonPrimitive|null|undefined; + optional?: NonPrimitive; +} + +// tsickle -> Closure type declarations + /** @type {NonPrimitive} */ +NonPrimitives.prototype.nonNull; + /** @type {(null|NonPrimitive)} */ +NonPrimitives.prototype.nullable; + /** @type {(undefined|NonPrimitive)} */ +NonPrimitives.prototype.undefinable; + /** @type {(undefined|null|NonPrimitive)} */ +NonPrimitives.prototype.nullableUndefinable; + /** @type {(undefined|NonPrimitive)} */ +NonPrimitives.prototype.optional; + diff --git a/test_files/optional/optional.js b/test_files/optional/optional.js index ef9e44645..bb9f71705 100644 --- a/test_files/optional/optional.js +++ b/test_files/optional/optional.js @@ -1,6 +1,6 @@ goog.module('test_files.optional.optional'); exports = {}; var module = module || {id: 'test_files/optional/optional.js'};/** * @param {number} x - * @param {string=} y + * @param {(undefined|string)=} y * @return {void} */ function optionalArgument(x, y) { @@ -9,7 +9,7 @@ optionalArgument(1); class OptionalTest { /** * @param {string} a - * @param {string=} b + * @param {(undefined|string)=} b */ constructor(a, b) { } diff --git a/test_files/optional/optional.tsickle.ts b/test_files/optional/optional.tsickle.ts index b16cd2487..a9db0d5e6 100644 --- a/test_files/optional/optional.tsickle.ts +++ b/test_files/optional/optional.tsickle.ts @@ -1,7 +1,7 @@ /** * @param {number} x - * @param {string=} y + * @param {(undefined|string)=} y * @return {void} */ function optionalArgument(x: number, y?: string) { @@ -11,7 +11,7 @@ optionalArgument(1); class OptionalTest { /** * @param {string} a - * @param {string=} b + * @param {(undefined|string)=} b */ constructor(a: string, b?: string) {} /** diff --git a/test_files/type/type.js b/test_files/type/type.js index 27ad9fe40..243bb5ad6 100644 --- a/test_files/type/type.js +++ b/test_files/type/type.js @@ -1,15 +1,15 @@ goog.module('test_files.type.type'); exports = {}; var module = module || {id: 'test_files/type/type.js'};let /** @type {?} */ typeAny; -let /** @type {Array} */ typeArr = null; -let /** @type {Array} */ typeArr2 = null; -let /** @type {Array>} */ typeNestedArr = null; +let /** @type {Array} */ typeArr; +let /** @type {Array} */ typeArr2; +let /** @type {Array>} */ typeNestedArr; let /** @type {{a: number, b: string}} */ typeObject = { a: 3, b: 'b' }; -let /** @type {Object} */ typeObject2 = null; -let /** @type {?} */ typeObject3 = null; -let /** @type {Object} */ typeObjectEmpty = null; +let /** @type {Object} */ typeObject2; +let /** @type {?} */ typeObject3; +let /** @type {Object} */ typeObjectEmpty; let /** @type {(string|boolean)} */ typeUnion = Math.random() > 0.5 ? false : ''; let /** @type {(string|boolean)} */ typeUnion2 = Math.random() > 0.5 ? false : ''; -let /** @type {{optional: (boolean|undefined)}} */ typeOptionalField = {}; -let /** @type {{optional: ((string|boolean)|undefined)}} */ typeOptionalUnion = {}; +let /** @type {{optional: ((undefined|boolean)|undefined)}} */ typeOptionalField = {}; +let /** @type {{optional: ((undefined|string|boolean)|undefined)}} */ typeOptionalUnion = {}; let /** @type {function(): void} */ typeFunc = function () { }; let /** @type {function(number, ?): string} */ typeFunc2 = function (a, b) { return ''; }; let /** @type {function(number, function(number): string): string} */ typeFunc3 = function (x, cb) { return ''; }; diff --git a/test_files/type/type.ts b/test_files/type/type.ts index b56cfae18..f14665221 100644 --- a/test_files/type/type.ts +++ b/test_files/type/type.ts @@ -5,13 +5,13 @@ interface Array { } let typeAny: any; -let typeArr: Array = null; -let typeArr2: any[] = null; -let typeNestedArr: {a:any}[][] = null; +let typeArr: Array; +let typeArr2: any[]; +let typeNestedArr: {a:any}[][]; let typeObject: {a:number, b:string} = {a:3, b:'b'}; -let typeObject2: {[key:string]: number} = null; -let typeObject3: {a:number, [key:string]: number} = null; -let typeObjectEmpty: {} = null; +let typeObject2: {[key:string]: number}; +let typeObject3: {a:number, [key:string]: number}; +let typeObjectEmpty: {}; let typeUnion: string|boolean = Math.random() > 0.5 ? false : ''; let typeUnion2: (string|boolean) = Math.random() > 0.5 ? false : ''; diff --git a/test_files/type/type.tsickle.ts b/test_files/type/type.tsickle.ts index a2ac0ecf2..419088ef8 100644 --- a/test_files/type/type.tsickle.ts +++ b/test_files/type/type.tsickle.ts @@ -9,18 +9,18 @@ interface Array { } let /** @type {?} */ typeAny: any; -let /** @type {Array} */ typeArr: Array = null; -let /** @type {Array} */ typeArr2: any[] = null; -let /** @type {Array>} */ typeNestedArr: {a:any}[][] = null; +let /** @type {Array} */ typeArr: Array; +let /** @type {Array} */ typeArr2: any[]; +let /** @type {Array>} */ typeNestedArr: {a:any}[][]; let /** @type {{a: number, b: string}} */ typeObject: {a:number, b:string} = {a:3, b:'b'}; -let /** @type {Object} */ typeObject2: {[key:string]: number} = null; -let /** @type {?} */ typeObject3: {a:number, [key:string]: number} = null; -let /** @type {Object} */ typeObjectEmpty: {} = null; +let /** @type {Object} */ typeObject2: {[key:string]: number}; +let /** @type {?} */ typeObject3: {a:number, [key:string]: number}; +let /** @type {Object} */ typeObjectEmpty: {}; let /** @type {(string|boolean)} */ typeUnion: string|boolean = Math.random() > 0.5 ? false : ''; let /** @type {(string|boolean)} */ typeUnion2: (string|boolean) = Math.random() > 0.5 ? false : ''; -let /** @type {{optional: (boolean|undefined)}} */ typeOptionalField: {optional?: boolean} = {}; -let /** @type {{optional: ((string|boolean)|undefined)}} */ typeOptionalUnion: {optional?: string|boolean} = {}; +let /** @type {{optional: ((undefined|boolean)|undefined)}} */ typeOptionalField: {optional?: boolean} = {}; +let /** @type {{optional: ((undefined|string|boolean)|undefined)}} */ typeOptionalUnion: {optional?: string|boolean} = {}; let /** @type {function(): void} */ typeFunc: () => void = function() {}; let /** @type {function(number, ?): string} */ typeFunc2: (a: number, b: any) => string = function(a, b) { return ''; }; diff --git a/test_files/type_and_value/type_and_value.js b/test_files/type_and_value/type_and_value.js index 74086b630..f5cf82e54 100644 --- a/test_files/type_and_value/type_and_value.js +++ b/test_files/type_and_value/type_and_value.js @@ -3,11 +3,11 @@ var conflict = goog.require('test_files.type_and_value.module'); // This test deals with symbols that are simultaneously types and values. // Use a browser built-in as both a type and a value. let /** @type {function(new: Document): ?} */ useBuiltInAsValue = Document; -let /** @type {Document} */ useBuiltInAsType = null; +let /** @type {Document} */ useBuiltInAsType; // Use a user-defined class as both a type and a value. let /** @type {?} */ useUserClassAsValue = conflict.Class; -let /** @type {conflict.Class} */ useUserClassAsType = null; +let /** @type {conflict.Class} */ useUserClassAsType; // Use a user-defined interface/value pair as both a type and a value. let /** @type {number} */ useAsValue = conflict.TypeAndValue; // Note: because of the conflict, we currently just use the type {?} here. -let /** @type {?} */ useAsType = null; +let /** @type {?} */ useAsType; diff --git a/test_files/type_and_value/type_and_value.ts b/test_files/type_and_value/type_and_value.ts index 8e52c5158..bb61efae8 100644 --- a/test_files/type_and_value/type_and_value.ts +++ b/test_files/type_and_value/type_and_value.ts @@ -4,13 +4,13 @@ import * as conflict from './module'; // Use a browser built-in as both a type and a value. let useBuiltInAsValue = Document; -let useBuiltInAsType: Document = null; +let useBuiltInAsType: Document; // Use a user-defined class as both a type and a value. let useUserClassAsValue = conflict.Class; -let useUserClassAsType: conflict.Class = null; +let useUserClassAsType: conflict.Class; // Use a user-defined interface/value pair as both a type and a value. let useAsValue = conflict.TypeAndValue; // Note: because of the conflict, we currently just use the type {?} here. -let useAsType: conflict.TypeAndValue = null; +let useAsType: conflict.TypeAndValue; diff --git a/test_files/type_and_value/type_and_value.tsickle.ts b/test_files/type_and_value/type_and_value.tsickle.ts index 32a98e038..4a9e8c895 100644 --- a/test_files/type_and_value/type_and_value.tsickle.ts +++ b/test_files/type_and_value/type_and_value.tsickle.ts @@ -7,13 +7,13 @@ import * as conflict from './module'; // Use a browser built-in as both a type and a value. let /** @type {function(new: Document): ?} */ useBuiltInAsValue = Document; -let /** @type {Document} */ useBuiltInAsType: Document = null; +let /** @type {Document} */ useBuiltInAsType: Document; // Use a user-defined class as both a type and a value. let /** @type {?} */ useUserClassAsValue = conflict.Class; -let /** @type {conflict.Class} */ useUserClassAsType: conflict.Class = null; +let /** @type {conflict.Class} */ useUserClassAsType: conflict.Class; // Use a user-defined interface/value pair as both a type and a value. let /** @type {number} */ useAsValue = conflict.TypeAndValue; // Note: because of the conflict, we currently just use the type {?} here. -let /** @type {?} */ useAsType: conflict.TypeAndValue = null; +let /** @type {?} */ useAsType: conflict.TypeAndValue; From ed55addc264f35562bf3b30062a33d84e9db8773 Mon Sep 17 00:00:00 2001 From: Martin Probst Date: Wed, 9 Nov 2016 17:42:43 -0800 Subject: [PATCH 2/4] Mark TypeScript types as non-nullable. All TypeScript types in strictNullChecks mode are non-nullable, where Closure types without a ! are treated as nullable. This change adds ! on all types except for types treated as never null primitives by Closure Compiler (number, string, function). --- src/tsickle.ts | 4 +- src/type-translator.ts | 38 +++++++++-------- test_files/abstract/abstract.js | 6 +-- test_files/abstract/abstract.tsickle.ts | 6 +-- test_files/declare/externs.js | 4 +- test_files/declare_class_overloads/externs.js | 12 +++--- test_files/decorator/decorator.js | 18 ++++---- test_files/decorator/decorator.tsickle.ts | 14 +++---- test_files/enum/enum.js | 2 +- test_files/enum/enum.tsickle.ts | 2 +- test_files/export/export_helper.tsickle.ts | 2 +- test_files/fields/fields.js | 2 +- test_files/fields/fields.tsickle.ts | 2 +- test_files/functions/functions.js | 2 +- test_files/functions/functions.tsickle.ts | 2 +- test_files/interface/interface.js | 4 +- test_files/interface/interface.tsickle.ts | 4 +- test_files/jsdoc_types/jsdoc_types.js | 22 +++++----- test_files/jsdoc_types/jsdoc_types.tsickle.ts | 22 +++++----- test_files/jsx/externs.js | 6 +-- test_files/jsx/jsx.js | 8 ++-- test_files/jsx/jsx.tsickle.tsx | 6 +-- test_files/jsx/jsx.tsx | 2 +- test_files/nullable/nullable.js | 42 ++++++++++--------- test_files/nullable/nullable.ts | 17 ++++++++ test_files/nullable/nullable.tsickle.ts | 16 +++---- test_files/optional/optional.js | 2 +- test_files/optional/optional.tsickle.ts | 2 +- test_files/type/type.js | 10 ++--- test_files/type/type.tsickle.ts | 10 ++--- test_files/type_and_value/type_and_value.js | 4 +- .../type_and_value/type_and_value.tsickle.ts | 4 +- 32 files changed, 161 insertions(+), 136 deletions(-) create mode 100644 test_files/nullable/nullable.ts diff --git a/src/tsickle.ts b/src/tsickle.ts index c2974e497..d43332c4f 100644 --- a/src/tsickle.ts +++ b/src/tsickle.ts @@ -328,7 +328,7 @@ class ClosureRewriter extends Rewriter { * object/array types. This is a workaround specifically for destructuring * bind patterns. */ - typeToClosure(context: ts.Node, type?: ts.Type, destructuring?: boolean): string { + typeToClosure(context: ts.Node, type?: ts.Type, destructuring = false): string { if (this.options.untyped) { return '?'; } @@ -339,7 +339,7 @@ class ClosureRewriter extends Rewriter { } let translator = new TypeTranslator(typeChecker, context, this.options.typeBlackListPaths); translator.warn = msg => this.debugWarn(context, msg); - return translator.translate(type, destructuring); + return translator.translate(type, true); } /** diff --git a/src/type-translator.ts b/src/type-translator.ts index d284c7447..1276f4913 100644 --- a/src/type-translator.ts +++ b/src/type-translator.ts @@ -153,7 +153,7 @@ export class TypeTranslator { * @param notNull When true, insert a ! before any type references. This * is to work around the difference between TS and Closure destructuring. */ - translate(type: ts.Type, notNull = false): string { + translate(type: ts.Type, notNull: boolean): string { // See the function `buildTypeDisplay` in the TypeScript compiler source // for guidance on a similar operation. @@ -199,7 +199,7 @@ export class TypeTranslator { this.warn('class has no symbol'); return '?'; } - return this.symbolToString(type.symbol); + return notNullPrefix + this.symbolToString(type.symbol); } else if (type.flags & ts.TypeFlags.Interface) { // Note: ts.InterfaceType has a typeParameters field, but that // specifies the parameters that the interface type *expects* @@ -221,7 +221,7 @@ export class TypeTranslator { return '?'; } } - return this.symbolToString(type.symbol); + return notNullPrefix + this.symbolToString(type.symbol); } else if (type.flags & ts.TypeFlags.Reference) { // A reference to another type, e.g. Array refers to Array. // Emit the referenced type and any type arguments. @@ -241,11 +241,11 @@ export class TypeTranslator { throw new Error( `reference loop in ${typeToDebugString(referenceType)} ${referenceType.flags}`); } - typeStr += notNullPrefix + this.translate(referenceType.target); + typeStr += this.translate(referenceType.target, notNull); } if (referenceType.typeArguments) { - let params = referenceType.typeArguments.map(t => this.translate(t, notNull)); - typeStr += isTuple ? `Array` : `<${params.join(', ')}>`; + let params = referenceType.typeArguments.map(t => this.translate(t, true)); + typeStr += isTuple ? notNullPrefix + `Array` : `<${params.join(', ')}>`; } return typeStr; } else if (type.flags & ts.TypeFlags.Anonymous) { @@ -259,7 +259,7 @@ export class TypeTranslator { } if (type.symbol.flags === ts.SymbolFlags.TypeLiteral) { - return notNullPrefix + this.translateTypeLiteral(type); + return this.translateTypeLiteral(type, notNull); } else if ( type.symbol.flags === ts.SymbolFlags.Function || type.symbol.flags === ts.SymbolFlags.Method) { @@ -272,7 +272,7 @@ export class TypeTranslator { return '?'; } else if (type.flags & ts.TypeFlags.Union) { let unionType = type as ts.UnionType; - let parts = unionType.types.map(t => this.translate(t)); + let parts = unionType.types.map(t => this.translate(t, notNull)); // In union types that include boolean literal and other literals can // end up repeating the same closure type. For example: true | boolean // will be translated to boolean | boolean. Remove duplicates to produce @@ -284,7 +284,8 @@ export class TypeTranslator { return '?'; } - private translateTypeLiteral(type: ts.Type): string { + private translateTypeLiteral(type: ts.Type, notNull: boolean): string { + const notNullPrefix = notNull ? '!' : ''; // Avoid infinite loops on recursive types. // It would be nice to just emit the name of the recursive type here, // but type.symbol doesn't seem to have the name here (perhaps something @@ -308,7 +309,8 @@ export class TypeTranslator { // (not expressible in Closure), nor multiple constructors (same). const params = this.convertParams(ctors[0]); const paramsStr = params.length ? (', ' + params.join(', ')) : ''; - return `function(new: ${this.translate(ctors[0].getReturnType())}${paramsStr}): ?`; + const constructedType = this.translate(ctors[0].getReturnType(), false); + return `function(new: ${constructedType}${paramsStr}): ?`; } for (let field of Object.keys(type.symbol.members)) { @@ -323,7 +325,7 @@ export class TypeTranslator { let member = type.symbol.members[field]; let isOptional = member.flags & ts.SymbolFlags.Optional; let memberType = - this.translate(this.typeChecker.getTypeOfSymbolAtLocation(member, this.node)); + this.translate(this.typeChecker.getTypeOfSymbolAtLocation(member, this.node), true); if (isOptional) { memberType = `(${memberType}|undefined)`; } @@ -349,13 +351,13 @@ export class TypeTranslator { } if (!valType) { this.warn('unknown index key type'); - return `Object`; + return notNullPrefix + `Object`; } - return `Object<${keyType},${this.translate(valType)}>`; + return notNullPrefix + `Object<${keyType},${this.translate(valType, true)}>`; } else if (!callable && !indexable) { // Special-case the empty object {} because Closure doesn't like it. // TODO(evanm): revisit this if it is a problem. - return 'Object'; + return notNullPrefix + 'Object'; } } @@ -373,7 +375,7 @@ export class TypeTranslator { let params = this.convertParams(sig); let typeStr = `function(${params.join(', ')})`; - let retType = this.translate(this.typeChecker.getReturnTypeOfSignature(sig)); + let retType = this.translate(this.typeChecker.getReturnTypeOfSignature(sig), true); if (retType) { typeStr += `: ${retType}`; } @@ -382,8 +384,10 @@ export class TypeTranslator { } private convertParams(sig: ts.Signature): string[] { - return sig.parameters.map( - param => this.translate(this.typeChecker.getTypeOfSymbolAtLocation(param, this.node))); + return sig.parameters.map(param => { + let paramType = this.typeChecker.getTypeOfSymbolAtLocation(param, this.node); + return this.translate(paramType, true); + }); } warn(msg: string) { diff --git a/test_files/abstract/abstract.js b/test_files/abstract/abstract.js index 3f8d095de..8ebfd7d32 100644 --- a/test_files/abstract/abstract.js +++ b/test_files/abstract/abstract.js @@ -14,7 +14,7 @@ class Base { publicAbstract() { } /** * @abstract - * @param {Array} x + * @param {!Array} x * @return {void} */ params(x) { } @@ -52,7 +52,7 @@ class Derived extends Base { */ publicAbstract() { } /** - * @param {Array} x + * @param {!Array} x * @return {void} */ params(x) { } @@ -65,4 +65,4 @@ class Derived extends Base { */ hasReturnType() { return 3; } } -let /** @type {Base} */ x = new Derived(); +let /** @type {!Base} */ x = new Derived(); diff --git a/test_files/abstract/abstract.tsickle.ts b/test_files/abstract/abstract.tsickle.ts index 33974e985..9e058a70a 100644 --- a/test_files/abstract/abstract.tsickle.ts +++ b/test_files/abstract/abstract.tsickle.ts @@ -15,7 +15,7 @@ simple() {} publicAbstract() {} /** * @abstract - * @param {Array} x + * @param {!Array} x * @return {void} */ params(x: number[]) {} @@ -54,7 +54,7 @@ simple() {} */ publicAbstract() {} /** - * @param {Array} x + * @param {!Array} x * @return {void} */ params(x: number[]): void { } @@ -68,4 +68,4 @@ noReturnType() {} hasReturnType(): number { return 3; } } -let /** @type {Base} */ x: Base = new Derived(); +let /** @type {!Base} */ x: Base = new Derived(); diff --git a/test_files/declare/externs.js b/test_files/declare/externs.js index d3c6fd53a..4da5bea43 100644 --- a/test_files/declare/externs.js +++ b/test_files/declare/externs.js @@ -84,14 +84,14 @@ Object.prototype.myMethod = function() {}; /** * @param {string} x - * @return {CodeMirror.Editor} + * @return {!CodeMirror.Editor} */ function CodeMirror(x) {} /** * @param {number} y * @param {string} x - * @return {CodeMirror.Editor} + * @return {!CodeMirror.Editor} */ function CodeMirror(y, x) {} diff --git a/test_files/declare_class_overloads/externs.js b/test_files/declare_class_overloads/externs.js index 278e9df52..f0d51bbbd 100644 --- a/test_files/declare_class_overloads/externs.js +++ b/test_files/declare_class_overloads/externs.js @@ -31,8 +31,8 @@ function MultipleConstructorsNamesAndTypes(a_or_b) {} * @param {number} a * @param {number} b * @param {number} c - * @param {(undefined|string)|(undefined|Array)} normal_or_vertexNormals - * @param {(undefined|boolean)|(undefined|Array)} color_or_vertexColors + * @param {(undefined|string)|(undefined|!Array)} normal_or_vertexNormals + * @param {(undefined|boolean)|(undefined|!Array)} color_or_vertexColors * @param {(undefined|number)} materialIndex */ function MultipleConstructorsComplexMatrix(a, b, c, normal_or_vertexNormals, color_or_vertexColors, materialIndex) {} @@ -40,14 +40,14 @@ function MultipleConstructorsComplexMatrix(a, b, c, normal_or_vertexNormals, col /** * @constructor * @struct - * @param {number|Array} a + * @param {number|!Array} a */ function MultipleConstructorsVariadic(a) {} /** * @constructor * @struct - * @param {Array|Array|string|number} points + * @param {!Array|!Array|string|number} points */ function MultipleConstructorsVariadicNames(points) {} @@ -97,7 +97,7 @@ OverloadReturnTypesWithVoid.prototype.overloaded = function(a, opt_b, opt_c) {}; function OverloadBigMix() {} /** - * @param {string|number|Array|OverloadBigMix} a_or_c_or_e_or_f + * @param {string|number|!Array|!OverloadBigMix} a_or_c_or_e_or_f * @param {number} opt_b * @return {void|number|boolean} */ @@ -115,7 +115,7 @@ OverloadValueOf.prototype.valueOf = function() {}; function Merged() {} /** - * @param {(string|number|Array)} a_or_c_or_e_or_f + * @param {(string|number|!Array)} a_or_c_or_e_or_f * @param {number} opt_b * @return {(number|boolean|void)} */ diff --git a/test_files/decorator/decorator.js b/test_files/decorator/decorator.js index c7acc4a56..653eba98f 100644 --- a/test_files/decorator/decorator.js +++ b/test_files/decorator/decorator.js @@ -1,11 +1,11 @@ goog.module('test_files.decorator.decorator'); exports = {}; var module = module || {id: 'test_files/decorator/decorator.js'};/** - * @param {Object} a + * @param {!Object} a * @param {string} b * @return {void} */ function decorator(a, b) { } /** - * @param {Object} a + * @param {!Object} a * @param {string} b * @return {void} */ @@ -31,16 +31,16 @@ DecoratorTest.propDecorators = { 'y': [{ type: annotationDecorator },], }; __decorate([ - decorator, + decorator, __metadata('design:type', Number) ], DecoratorTest.prototype, "x", void 0); function DecoratorTest_tsickle_Closure_declarations() { - /** @type {Array} */ + /** @type {!Array} */ DecoratorTest.decorators; /** @nocollapse - @type {Array<{type: ?, decorators: (Array|undefined)}>} */ + @type {!Array<(null|{type: ?, decorators: ((undefined|!Array)|undefined)})>} */ DecoratorTest.ctorParameters; - /** @type {Object>} */ + /** @type {!Object>} */ DecoratorTest.propDecorators; /** @type {number} */ DecoratorTest.prototype.x; @@ -50,7 +50,7 @@ function DecoratorTest_tsickle_Closure_declarations() { let DecoratedClass = class DecoratedClass { }; DecoratedClass = __decorate([ - classDecorator, + classDecorator, __metadata('design:paramtypes', []) ], DecoratedClass); function DecoratedClass_tsickle_Closure_declarations() { @@ -59,7 +59,7 @@ function DecoratedClass_tsickle_Closure_declarations() { } /** @record */ function DecoratorInvocation() { } -/** @type {Function} */ +/** @type {!Function} */ DecoratorInvocation.prototype.type; -/** @type {Array} */ +/** @type {(undefined|!Array)} */ DecoratorInvocation.prototype.args; diff --git a/test_files/decorator/decorator.tsickle.ts b/test_files/decorator/decorator.tsickle.ts index 851e3f72d..09278227c 100644 --- a/test_files/decorator/decorator.tsickle.ts +++ b/test_files/decorator/decorator.tsickle.ts @@ -1,12 +1,12 @@ /** - * @param {Object} a + * @param {!Object} a * @param {string} b * @return {void} */ function decorator(a: Object, b: string) {} /** - * @param {Object} a + * @param {!Object} a * @param {string} b * @return {void} */ @@ -39,12 +39,12 @@ static propDecorators: {[key: string]: DecoratorInvocation[]} = { } function DecoratorTest_tsickle_Closure_declarations() { - /** @type {Array} */ + /** @type {!Array} */ DecoratorTest.decorators; /** @nocollapse - @type {Array<(null|{type: ?, decorators: ((undefined|Array)|undefined)})>} */ + @type {!Array<(null|{type: ?, decorators: ((undefined|!Array)|undefined)})>} */ DecoratorTest.ctorParameters; - /** @type {Object>} */ + /** @type {!Object>} */ DecoratorTest.propDecorators; /** @type {number} */ DecoratorTest.prototype.x; @@ -65,9 +65,9 @@ DecoratedClass.prototype.z; /** @record */ function DecoratorInvocation() {} - /** @type {Function} */ + /** @type {!Function} */ DecoratorInvocation.prototype.type; - /** @type {(undefined|Array)} */ + /** @type {(undefined|!Array)} */ DecoratorInvocation.prototype.args; diff --git a/test_files/enum/enum.js b/test_files/enum/enum.js index c2f46eeae..28b90c310 100644 --- a/test_files/enum/enum.js +++ b/test_files/enum/enum.js @@ -1,6 +1,6 @@ goog.module('test_files.enum.enum'); exports = {}; var module = module || {id: 'test_files/enum/enum.js'}; // Line with a missing semicolon should not break the following enum. -const /** @type {Array} */ EnumTestMissingSemi = []; +const /** @type {!Array} */ EnumTestMissingSemi = []; let EnumTest1 = {}; /** @type {number} */ EnumTest1.XYZ = 0; diff --git a/test_files/enum/enum.tsickle.ts b/test_files/enum/enum.tsickle.ts index 7e3c665db..9f67cab36 100644 --- a/test_files/enum/enum.tsickle.ts +++ b/test_files/enum/enum.tsickle.ts @@ -1,7 +1,7 @@ Warning at test_files/enum/enum.ts:2:7: unhandled type {type flags:0x2000 } ==== // Line with a missing semicolon should not break the following enum. -const /** @type {Array} */ EnumTestMissingSemi = [] +const /** @type {!Array} */ EnumTestMissingSemi = [] type EnumTest1 = number; let EnumTest1: any = {}; /** @type {number} */ diff --git a/test_files/export/export_helper.tsickle.ts b/test_files/export/export_helper.tsickle.ts index 47f399d74..d8683ce84 100644 --- a/test_files/export/export_helper.tsickle.ts +++ b/test_files/export/export_helper.tsickle.ts @@ -10,6 +10,6 @@ Bar.prototype.barField; export interface Bar { barField: number; } -export var /** @type {Bar} */ export3: Bar; +export var /** @type {!Bar} */ export3: Bar; export let /** @type {number} */ export5 = 3; diff --git a/test_files/fields/fields.js b/test_files/fields/fields.js index 05febb313..263b0dbb6 100644 --- a/test_files/fields/fields.js +++ b/test_files/fields/fields.js @@ -26,7 +26,7 @@ function FieldsTest_tsickle_Closure_declarations() { /** @type {number} */ FieldsTest.prototype.field3; } -let /** @type {FieldsTest} */ fieldsTest = new FieldsTest(3); +let /** @type {!FieldsTest} */ fieldsTest = new FieldsTest(3); // Ensure the type is understood by Closure. fieldsTest.field1 = 'hi'; let /** @type {?} */ AnonymousClass = class { diff --git a/test_files/fields/fields.tsickle.ts b/test_files/fields/fields.tsickle.ts index a92c57e83..8d06c67aa 100644 --- a/test_files/fields/fields.tsickle.ts +++ b/test_files/fields/fields.tsickle.ts @@ -33,7 +33,7 @@ FieldsTest.prototype.field3; } -let /** @type {FieldsTest} */ fieldsTest = new FieldsTest(3); +let /** @type {!FieldsTest} */ fieldsTest = new FieldsTest(3); // Ensure the type is understood by Closure. fieldsTest.field1 = 'hi'; diff --git a/test_files/functions/functions.js b/test_files/functions/functions.js index 5391d6c20..90e71e01d 100644 --- a/test_files/functions/functions.js +++ b/test_files/functions/functions.js @@ -26,7 +26,7 @@ function Test4(a) { return "a"; } /** - * @param {!{a: number, b: number}} __0 + * @param {{a: number, b: number}} __0 * @return {void} */ function Destructuring({ a, b }) { } diff --git a/test_files/functions/functions.tsickle.ts b/test_files/functions/functions.tsickle.ts index 412fb4f0a..47ac71c91 100644 --- a/test_files/functions/functions.tsickle.ts +++ b/test_files/functions/functions.tsickle.ts @@ -35,7 +35,7 @@ function Test4(a: any): string { return "a"; } /** - * @param {!{a: number, b: number}} __0 + * @param {{a: number, b: number}} __0 * @return {void} */ function Destructuring({a, b}: {a: number, b: number}) {} diff --git a/test_files/interface/interface.js b/test_files/interface/interface.js index 63be54ebe..04c72478e 100644 --- a/test_files/interface/interface.js +++ b/test_files/interface/interface.js @@ -5,13 +5,13 @@ Point.prototype.x; /** @type {number} */ Point.prototype.y; /** - * @param {Point} p + * @param {!Point} p * @return {number} */ function usePoint(p) { return p.x + p.y; } -let /** @type {Point} */ p = { x: 1, y: 1 }; +let /** @type {!Point} */ p = { x: 1, y: 1 }; usePoint(p); usePoint({ x: 1, y: 1 }); /** @record */ diff --git a/test_files/interface/interface.tsickle.ts b/test_files/interface/interface.tsickle.ts index 6200416af..4726c28b0 100644 --- a/test_files/interface/interface.tsickle.ts +++ b/test_files/interface/interface.tsickle.ts @@ -10,14 +10,14 @@ interface Point { y: number; } /** - * @param {Point} p + * @param {!Point} p * @return {number} */ function usePoint(p: Point): number { return p.x + p.y; } -let /** @type {Point} */ p: Point = {x:1, y:1}; +let /** @type {!Point} */ p: Point = {x:1, y:1}; usePoint(p); usePoint({x:1, y:1}); /** @record */ diff --git a/test_files/jsdoc_types/jsdoc_types.js b/test_files/jsdoc_types/jsdoc_types.js index 995f176c4..cbe10161a 100644 --- a/test_files/jsdoc_types/jsdoc_types.js +++ b/test_files/jsdoc_types/jsdoc_types.js @@ -16,21 +16,21 @@ const DefaultClass = default_1.default; /* local alias for Closure JSDoc */ var nevertyped_1 = goog.require('test_files.jsdoc_types.nevertyped'); const NeverTyped = nevertyped_1.NeverTyped; /* local alias for Closure JSDoc */ // Check that imported types get the proper names in JSDoc. -let /** @type {module1.Class} */ useNamespacedClass = new module1.Class(); -let /** @type {module1.Class} */ useNamespacedClassAsType; -let /** @type {module1.Interface} */ useNamespacedType; +let /** @type {!module1.Class} */ useNamespacedClass = new module1.Class(); +let /** @type {!module1.Class} */ useNamespacedClassAsType; +let /** @type {!module1.Interface} */ useNamespacedType; // Should be references to the symbols in module2, perhaps via locals. -let /** @type {ClassOne} */ useLocalClass = new module2_1.ClassOne(); -let /** @type {ClassOne} */ useLocalClassRenamed = new module2_1.ClassOne(); -let /** @type {RenamedClassTwo} */ useLocalClassRenamedTwo = new module2_1.ClassTwo(); -let /** @type {ClassOne} */ useLocalClassAsTypeRenamed; -let /** @type {Interface} */ useLocalInterface; -let /** @type {ClassWithParams} */ useClassWithParams; +let /** @type {!ClassOne} */ useLocalClass = new module2_1.ClassOne(); +let /** @type {!ClassOne} */ useLocalClassRenamed = new module2_1.ClassOne(); +let /** @type {!RenamedClassTwo} */ useLocalClassRenamedTwo = new module2_1.ClassTwo(); +let /** @type {!ClassOne} */ useLocalClassAsTypeRenamed; +let /** @type {!Interface} */ useLocalInterface; +let /** @type {!ClassWithParams} */ useClassWithParams; // This is purely a value; it doesn't need renaming. let /** @type {number} */ useLocalValue = module2_1.value; // Check a default import. -let /** @type {DefaultClass} */ useDefaultClass = new default_1.default(); -let /** @type {DefaultClass} */ useDefaultClassAsType; +let /** @type {!DefaultClass} */ useDefaultClass = new default_1.default(); +let /** @type {!DefaultClass} */ useDefaultClassAsType; // NeverTyped should be {?}, even in typed mode. let /** @type {?} */ useNeverTyped; let /** @type {(string|?)} */ useNeverTyped2; diff --git a/test_files/jsdoc_types/jsdoc_types.tsickle.ts b/test_files/jsdoc_types/jsdoc_types.tsickle.ts index 4a7211ef3..14da2777c 100644 --- a/test_files/jsdoc_types/jsdoc_types.tsickle.ts +++ b/test_files/jsdoc_types/jsdoc_types.tsickle.ts @@ -17,24 +17,24 @@ import {NeverTyped} from './nevertyped'; const NeverTyped: NeverTypeCheckMe = NeverTyped; /* local alias for Closure JSDoc */ // Check that imported types get the proper names in JSDoc. -let /** @type {module1.Class} */ useNamespacedClass = new module1.Class(); -let /** @type {module1.Class} */ useNamespacedClassAsType: module1.Class; -let /** @type {module1.Interface} */ useNamespacedType: module1.Interface; +let /** @type {!module1.Class} */ useNamespacedClass = new module1.Class(); +let /** @type {!module1.Class} */ useNamespacedClassAsType: module1.Class; +let /** @type {!module1.Interface} */ useNamespacedType: module1.Interface; // Should be references to the symbols in module2, perhaps via locals. -let /** @type {ClassOne} */ useLocalClass = new ClassOne(); -let /** @type {ClassOne} */ useLocalClassRenamed = new RenamedClassOne(); -let /** @type {RenamedClassTwo} */ useLocalClassRenamedTwo = new RenamedClassTwo(); -let /** @type {ClassOne} */ useLocalClassAsTypeRenamed: RenamedClassOne; -let /** @type {Interface} */ useLocalInterface: Interface; -let /** @type {ClassWithParams} */ useClassWithParams: ClassWithParams; +let /** @type {!ClassOne} */ useLocalClass = new ClassOne(); +let /** @type {!ClassOne} */ useLocalClassRenamed = new RenamedClassOne(); +let /** @type {!RenamedClassTwo} */ useLocalClassRenamedTwo = new RenamedClassTwo(); +let /** @type {!ClassOne} */ useLocalClassAsTypeRenamed: RenamedClassOne; +let /** @type {!Interface} */ useLocalInterface: Interface; +let /** @type {!ClassWithParams} */ useClassWithParams: ClassWithParams; // This is purely a value; it doesn't need renaming. let /** @type {number} */ useLocalValue = value; // Check a default import. -let /** @type {DefaultClass} */ useDefaultClass = new DefaultClass(); -let /** @type {DefaultClass} */ useDefaultClassAsType: DefaultClass; +let /** @type {!DefaultClass} */ useDefaultClass = new DefaultClass(); +let /** @type {!DefaultClass} */ useDefaultClassAsType: DefaultClass; // NeverTyped should be {?}, even in typed mode. let /** @type {?} */ useNeverTyped: NeverTyped; diff --git a/test_files/jsx/externs.js b/test_files/jsx/externs.js index 2db44504a..43f230ba9 100644 --- a/test_files/jsx/externs.js +++ b/test_files/jsx/externs.js @@ -20,13 +20,13 @@ var React = {}; /** * @param {...?} args - * @return {Element} + * @return {!Element} */ React.createElement = function(args) {}; /** - * @param {JSX.Element} element - * @param {HTMLElement} node + * @param {!JSX.Element} element + * @param {!HTMLElement} node * @return {void} */ React.render = function(element, node) {}; diff --git a/test_files/jsx/jsx.js b/test_files/jsx/jsx.js index 480720188..f65cdc1ae 100644 --- a/test_files/jsx/jsx.js +++ b/test_files/jsx/jsx.js @@ -1,7 +1,7 @@ -goog.module('test_files.jsx.jsx'); exports = {}; var module = module || {id: 'test_files/jsx/jsx.js'};let /** @type {JSX.Element} */ simple = React.createElement("div", null); +goog.module('test_files.jsx.jsx'); exports = {}; var module = module || {id: 'test_files/jsx/jsx.js'};let /** @type {!JSX.Element} */ simple = React.createElement("div", null); let /** @type {string} */ hello = 'hello'; -let /** @type {JSX.Element} */ helloDiv = React.createElement("div", null, - hello, - "hello, world", +let /** @type {!JSX.Element} */ helloDiv = React.createElement("div", null, + hello, + "hello, world", React.createElement(Component, null)); React.render(helloDiv, document.body); diff --git a/test_files/jsx/jsx.tsickle.tsx b/test_files/jsx/jsx.tsickle.tsx index d7a6a56b8..fd91160e9 100644 --- a/test_files/jsx/jsx.tsickle.tsx +++ b/test_files/jsx/jsx.tsickle.tsx @@ -17,14 +17,14 @@ declare module React { // Fake a subcomponent, just to exercise components within components. declare var Component: any; -let /** @type {JSX.Element} */ simple =
; +let /** @type {!JSX.Element} */ simple =
; let /** @type {string} */ hello = 'hello'; -let /** @type {JSX.Element} */ helloDiv =
+let /** @type {!JSX.Element} */ helloDiv =
{hello} hello, world
; -React.render(helloDiv, document.body); +React.render(helloDiv, document.body!); diff --git a/test_files/jsx/jsx.tsx b/test_files/jsx/jsx.tsx index 6c9aba126..5b938a99f 100644 --- a/test_files/jsx/jsx.tsx +++ b/test_files/jsx/jsx.tsx @@ -26,5 +26,5 @@ let helloDiv =
; -React.render(helloDiv, document.body); +React.render(helloDiv, document.body!); diff --git a/test_files/nullable/nullable.js b/test_files/nullable/nullable.js index 2e1c2a859..e9aa77a01 100644 --- a/test_files/nullable/nullable.js +++ b/test_files/nullable/nullable.js @@ -1,26 +1,28 @@ goog.module('test_files.nullable.nullable');var module = module || {id: 'test_files/nullable/nullable.js'};class Primitives { } -// tsickle -> Closure type declarations -/** @type {(null|string)} */ -Primitives.prototype.nullable; -/** @type {(undefined|number)} */ -Primitives.prototype.undefinable; -/** @type {(undefined|null|string)} */ -Primitives.prototype.nullableUndefinable; -/** @type {(undefined|string)} */ -Primitives.prototype.optional; +function Primitives_tsickle_Closure_declarations() { + /** @type {(null|string)} */ + Primitives.prototype.nullable; + /** @type {(undefined|number)} */ + Primitives.prototype.undefinable; + /** @type {(undefined|null|string)} */ + Primitives.prototype.nullableUndefinable; + /** @type {(undefined|string)} */ + Primitives.prototype.optional; +} class NonPrimitive { } class NonPrimitives { } -// tsickle -> Closure type declarations -/** @type {NonPrimitive} */ -NonPrimitives.prototype.nonNull; -/** @type {(null|NonPrimitive)} */ -NonPrimitives.prototype.nullable; -/** @type {(undefined|NonPrimitive)} */ -NonPrimitives.prototype.undefinable; -/** @type {(undefined|null|NonPrimitive)} */ -NonPrimitives.prototype.nullableUndefinable; -/** @type {(undefined|NonPrimitive)} */ -NonPrimitives.prototype.optional; +function NonPrimitives_tsickle_Closure_declarations() { + /** @type {!NonPrimitive} */ + NonPrimitives.prototype.nonNull; + /** @type {(null|!NonPrimitive)} */ + NonPrimitives.prototype.nullable; + /** @type {(undefined|!NonPrimitive)} */ + NonPrimitives.prototype.undefinable; + /** @type {(undefined|null|!NonPrimitive)} */ + NonPrimitives.prototype.nullableUndefinable; + /** @type {(undefined|!NonPrimitive)} */ + NonPrimitives.prototype.optional; +} diff --git a/test_files/nullable/nullable.ts b/test_files/nullable/nullable.ts new file mode 100644 index 000000000..076885573 --- /dev/null +++ b/test_files/nullable/nullable.ts @@ -0,0 +1,17 @@ + +class Primitives { + nullable: string|null; + undefinable: number|undefined; + nullableUndefinable: string|null|undefined; + optional?: string; +} + +class NonPrimitive {} + +class NonPrimitives { + nonNull: NonPrimitive; + nullable: NonPrimitive|null; + undefinable: NonPrimitive|undefined; + nullableUndefinable: NonPrimitive|null|undefined; + optional?: NonPrimitive; +} diff --git a/test_files/nullable/nullable.tsickle.ts b/test_files/nullable/nullable.tsickle.ts index 6ebee6f75..5db4f6eec 100644 --- a/test_files/nullable/nullable.tsickle.ts +++ b/test_files/nullable/nullable.tsickle.ts @@ -6,7 +6,7 @@ class Primitives { optional?: string; } -// tsickle -> Closure type declarations +function Primitives_tsickle_Closure_declarations() { /** @type {(null|string)} */ Primitives.prototype.nullable; /** @type {(undefined|number)} */ @@ -15,6 +15,7 @@ Primitives.prototype.undefinable; Primitives.prototype.nullableUndefinable; /** @type {(undefined|string)} */ Primitives.prototype.optional; +} class NonPrimitive {} @@ -27,15 +28,16 @@ class NonPrimitives { optional?: NonPrimitive; } -// tsickle -> Closure type declarations - /** @type {NonPrimitive} */ +function NonPrimitives_tsickle_Closure_declarations() { + /** @type {!NonPrimitive} */ NonPrimitives.prototype.nonNull; - /** @type {(null|NonPrimitive)} */ + /** @type {(null|!NonPrimitive)} */ NonPrimitives.prototype.nullable; - /** @type {(undefined|NonPrimitive)} */ + /** @type {(undefined|!NonPrimitive)} */ NonPrimitives.prototype.undefinable; - /** @type {(undefined|null|NonPrimitive)} */ + /** @type {(undefined|null|!NonPrimitive)} */ NonPrimitives.prototype.nullableUndefinable; - /** @type {(undefined|NonPrimitive)} */ + /** @type {(undefined|!NonPrimitive)} */ NonPrimitives.prototype.optional; +} diff --git a/test_files/optional/optional.js b/test_files/optional/optional.js index bb9f71705..2a86337b9 100644 --- a/test_files/optional/optional.js +++ b/test_files/optional/optional.js @@ -19,5 +19,5 @@ class OptionalTest { */ method(c = 'hi') { } } -let /** @type {OptionalTest} */ optionalTest = new OptionalTest('a'); +let /** @type {!OptionalTest} */ optionalTest = new OptionalTest('a'); optionalTest.method(); diff --git a/test_files/optional/optional.tsickle.ts b/test_files/optional/optional.tsickle.ts index a9db0d5e6..bef57188a 100644 --- a/test_files/optional/optional.tsickle.ts +++ b/test_files/optional/optional.tsickle.ts @@ -21,5 +21,5 @@ constructor(a: string, b?: string) {} method(c: string = 'hi') {} } -let /** @type {OptionalTest} */ optionalTest = new OptionalTest('a'); +let /** @type {!OptionalTest} */ optionalTest = new OptionalTest('a'); optionalTest.method(); diff --git a/test_files/type/type.js b/test_files/type/type.js index 243bb5ad6..a38b207d1 100644 --- a/test_files/type/type.js +++ b/test_files/type/type.js @@ -1,11 +1,11 @@ goog.module('test_files.type.type'); exports = {}; var module = module || {id: 'test_files/type/type.js'};let /** @type {?} */ typeAny; -let /** @type {Array} */ typeArr; -let /** @type {Array} */ typeArr2; -let /** @type {Array>} */ typeNestedArr; +let /** @type {!Array} */ typeArr; +let /** @type {!Array} */ typeArr2; +let /** @type {!Array>} */ typeNestedArr; let /** @type {{a: number, b: string}} */ typeObject = { a: 3, b: 'b' }; -let /** @type {Object} */ typeObject2; +let /** @type {!Object} */ typeObject2; let /** @type {?} */ typeObject3; -let /** @type {Object} */ typeObjectEmpty; +let /** @type {!Object} */ typeObjectEmpty; let /** @type {(string|boolean)} */ typeUnion = Math.random() > 0.5 ? false : ''; let /** @type {(string|boolean)} */ typeUnion2 = Math.random() > 0.5 ? false : ''; let /** @type {{optional: ((undefined|boolean)|undefined)}} */ typeOptionalField = {}; diff --git a/test_files/type/type.tsickle.ts b/test_files/type/type.tsickle.ts index 419088ef8..752cc3bc4 100644 --- a/test_files/type/type.tsickle.ts +++ b/test_files/type/type.tsickle.ts @@ -9,13 +9,13 @@ interface Array { } let /** @type {?} */ typeAny: any; -let /** @type {Array} */ typeArr: Array; -let /** @type {Array} */ typeArr2: any[]; -let /** @type {Array>} */ typeNestedArr: {a:any}[][]; +let /** @type {!Array} */ typeArr: Array; +let /** @type {!Array} */ typeArr2: any[]; +let /** @type {!Array>} */ typeNestedArr: {a:any}[][]; let /** @type {{a: number, b: string}} */ typeObject: {a:number, b:string} = {a:3, b:'b'}; -let /** @type {Object} */ typeObject2: {[key:string]: number}; +let /** @type {!Object} */ typeObject2: {[key:string]: number}; let /** @type {?} */ typeObject3: {a:number, [key:string]: number}; -let /** @type {Object} */ typeObjectEmpty: {}; +let /** @type {!Object} */ typeObjectEmpty: {}; let /** @type {(string|boolean)} */ typeUnion: string|boolean = Math.random() > 0.5 ? false : ''; let /** @type {(string|boolean)} */ typeUnion2: (string|boolean) = Math.random() > 0.5 ? false : ''; diff --git a/test_files/type_and_value/type_and_value.js b/test_files/type_and_value/type_and_value.js index f5cf82e54..bdf6fed67 100644 --- a/test_files/type_and_value/type_and_value.js +++ b/test_files/type_and_value/type_and_value.js @@ -3,10 +3,10 @@ var conflict = goog.require('test_files.type_and_value.module'); // This test deals with symbols that are simultaneously types and values. // Use a browser built-in as both a type and a value. let /** @type {function(new: Document): ?} */ useBuiltInAsValue = Document; -let /** @type {Document} */ useBuiltInAsType; +let /** @type {!Document} */ useBuiltInAsType; // Use a user-defined class as both a type and a value. let /** @type {?} */ useUserClassAsValue = conflict.Class; -let /** @type {conflict.Class} */ useUserClassAsType; +let /** @type {!conflict.Class} */ useUserClassAsType; // Use a user-defined interface/value pair as both a type and a value. let /** @type {number} */ useAsValue = conflict.TypeAndValue; // Note: because of the conflict, we currently just use the type {?} here. diff --git a/test_files/type_and_value/type_and_value.tsickle.ts b/test_files/type_and_value/type_and_value.tsickle.ts index 4a9e8c895..dfaab4315 100644 --- a/test_files/type_and_value/type_and_value.tsickle.ts +++ b/test_files/type_and_value/type_and_value.tsickle.ts @@ -7,11 +7,11 @@ import * as conflict from './module'; // Use a browser built-in as both a type and a value. let /** @type {function(new: Document): ?} */ useBuiltInAsValue = Document; -let /** @type {Document} */ useBuiltInAsType: Document; +let /** @type {!Document} */ useBuiltInAsType: Document; // Use a user-defined class as both a type and a value. let /** @type {?} */ useUserClassAsValue = conflict.Class; -let /** @type {conflict.Class} */ useUserClassAsType: conflict.Class; +let /** @type {!conflict.Class} */ useUserClassAsType: conflict.Class; // Use a user-defined interface/value pair as both a type and a value. let /** @type {number} */ useAsValue = conflict.TypeAndValue; From 427ab3fe9e58fd5e3d1f31f46f641683ef1b5412 Mon Sep 17 00:00:00 2001 From: Martin Probst Date: Thu, 10 Nov 2016 16:12:33 -0800 Subject: [PATCH 3/4] Support translating foo! expressions into type asserts. --- src/tsickle.ts | 24 ++++++++++++++++++++++-- src/type-translator.ts | 2 +- test_files/decorator/decorator.js | 4 ++-- test_files/jsx/jsx.js | 8 ++++---- test_files/jsx/jsx.tsickle.tsx | 2 +- test_files/nullable/nullable.js | 9 ++++++++- test_files/nullable/nullable.ts | 5 +++++ test_files/nullable/nullable.tsickle.ts | 8 ++++++++ 8 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/tsickle.ts b/src/tsickle.ts index d43332c4f..55dbd8a8e 100644 --- a/src/tsickle.ts +++ b/src/tsickle.ts @@ -310,12 +310,12 @@ class ClosureRewriter extends Rewriter { } /** Emits a type annotation in JSDoc, or {?} if the type is unavailable. */ - emitJSDocType(node: ts.Node, additionalDocTag?: string) { + emitJSDocType(node: ts.Node, additionalDocTag?: string, type?: ts.Type) { this.emit(' /**'); if (additionalDocTag) { this.emit(' ' + additionalDocTag); } - this.emit(` @type {${this.typeToClosure(node)}} */`); + this.emit(` @type {${this.typeToClosure(node, type)}} */`); } /** @@ -532,10 +532,30 @@ class Annotator extends ClosureRewriter { // When TypeScript emits JS, it removes one layer of "redundant" // parens, but we need them for the Closure type assertion. Work // around this by using two parens. See test_files/coerce.*. + // TODO: the comment is currently dropped from pure assignments due to + // https://github.com/Microsoft/TypeScript/issues/9873 this.emit('(('); this.writeNode(node); this.emit('))'); return true; + case ts.SyntaxKind.NonNullExpression: + const nnexpr = node as ts.NonNullExpression; + let type = this.program.getTypeChecker().getTypeAtLocation(nnexpr.expression); + if (type.flags & ts.TypeFlags.Union) { + const nonNullUnion = + (type as ts.UnionType) + .types.filter( + t => (t.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0); + const typeCopy = Object.assign({}, type as ts.UnionType); + typeCopy.types = nonNullUnion; + type = typeCopy; + } + this.emitJSDocType(nnexpr, undefined, type); + // See comment above. + this.emit('(('); + this.writeNode(nnexpr.expression); + this.emit('))'); + return true; default: break; } diff --git a/src/type-translator.ts b/src/type-translator.ts index 1276f4913..4574a34f9 100644 --- a/src/type-translator.ts +++ b/src/type-translator.ts @@ -272,7 +272,7 @@ export class TypeTranslator { return '?'; } else if (type.flags & ts.TypeFlags.Union) { let unionType = type as ts.UnionType; - let parts = unionType.types.map(t => this.translate(t, notNull)); + let parts = unionType.types.map(t => this.translate(t, true)); // In union types that include boolean literal and other literals can // end up repeating the same closure type. For example: true | boolean // will be translated to boolean | boolean. Remove duplicates to produce diff --git a/test_files/decorator/decorator.js b/test_files/decorator/decorator.js index 653eba98f..b8c07ca5e 100644 --- a/test_files/decorator/decorator.js +++ b/test_files/decorator/decorator.js @@ -31,7 +31,7 @@ DecoratorTest.propDecorators = { 'y': [{ type: annotationDecorator },], }; __decorate([ - decorator, + decorator, __metadata('design:type', Number) ], DecoratorTest.prototype, "x", void 0); function DecoratorTest_tsickle_Closure_declarations() { @@ -50,7 +50,7 @@ function DecoratorTest_tsickle_Closure_declarations() { let DecoratedClass = class DecoratedClass { }; DecoratedClass = __decorate([ - classDecorator, + classDecorator, __metadata('design:paramtypes', []) ], DecoratedClass); function DecoratedClass_tsickle_Closure_declarations() { diff --git a/test_files/jsx/jsx.js b/test_files/jsx/jsx.js index f65cdc1ae..5f58d6cfd 100644 --- a/test_files/jsx/jsx.js +++ b/test_files/jsx/jsx.js @@ -1,7 +1,7 @@ goog.module('test_files.jsx.jsx'); exports = {}; var module = module || {id: 'test_files/jsx/jsx.js'};let /** @type {!JSX.Element} */ simple = React.createElement("div", null); let /** @type {string} */ hello = 'hello'; -let /** @type {!JSX.Element} */ helloDiv = React.createElement("div", null, - hello, - "hello, world", +let /** @type {!JSX.Element} */ helloDiv = React.createElement("div", null, + hello, + "hello, world", React.createElement(Component, null)); -React.render(helloDiv, document.body); +React.render(helloDiv, /** @type {!HTMLElement} */ ((document.body))); diff --git a/test_files/jsx/jsx.tsickle.tsx b/test_files/jsx/jsx.tsickle.tsx index fd91160e9..02ce95e5c 100644 --- a/test_files/jsx/jsx.tsickle.tsx +++ b/test_files/jsx/jsx.tsickle.tsx @@ -26,5 +26,5 @@ let /** @type {!JSX.Element} */ helloDiv =
; -React.render(helloDiv, document.body!); +React.render(helloDiv, /** @type {!HTMLElement} */(( document.body))); diff --git a/test_files/nullable/nullable.js b/test_files/nullable/nullable.js index e9aa77a01..5adf20e2c 100644 --- a/test_files/nullable/nullable.js +++ b/test_files/nullable/nullable.js @@ -1,4 +1,4 @@ -goog.module('test_files.nullable.nullable');var module = module || {id: 'test_files/nullable/nullable.js'};class Primitives { +goog.module('test_files.nullable.nullable'); exports = {}; var module = module || {id: 'test_files/nullable/nullable.js'};class Primitives { } function Primitives_tsickle_Closure_declarations() { /** @type {(null|string)} */ @@ -26,3 +26,10 @@ function NonPrimitives_tsickle_Closure_declarations() { /** @type {(undefined|!NonPrimitive)} */ NonPrimitives.prototype.optional; } +/** + * @param {(string|number)} val + * @return {void} + */ +function takesNonNullable(val) { } +let /** @type {{field: (null|string|number)}} */ x = { field: null }; +takesNonNullable(/** @type {(string|number)} */ ((x.field))); diff --git a/test_files/nullable/nullable.ts b/test_files/nullable/nullable.ts index 076885573..cdd9562ae 100644 --- a/test_files/nullable/nullable.ts +++ b/test_files/nullable/nullable.ts @@ -15,3 +15,8 @@ class NonPrimitives { nullableUndefinable: NonPrimitive|null|undefined; optional?: NonPrimitive; } + +function takesNonNullable(val: string|number) {} + +let x: {field: string | null | number} = {field: null}; +takesNonNullable(x.field!); diff --git a/test_files/nullable/nullable.tsickle.ts b/test_files/nullable/nullable.tsickle.ts index 5db4f6eec..a964d4e6b 100644 --- a/test_files/nullable/nullable.tsickle.ts +++ b/test_files/nullable/nullable.tsickle.ts @@ -41,3 +41,11 @@ NonPrimitives.prototype.nullableUndefinable; NonPrimitives.prototype.optional; } +/** + * @param {(string|number)} val + * @return {void} + */ +function takesNonNullable(val: string|number) {} + +let /** @type {{field: (null|string|number)}} */ x: {field: string | null | number} = {field: null}; +takesNonNullable( /** @type {(string|number)} */((x.field))); From fbad0fdb0fafaf9b2a72a70b3821d5cd9160d11d Mon Sep 17 00:00:00 2001 From: Martin Probst Date: Fri, 11 Nov 2016 11:00:56 -0800 Subject: [PATCH 4/4] Remove the special case for optional members. These types are covered by TypeScript including |undefined in a union type in strictNullChecks mode. --- src/type-translator.ts | 5 +---- test_files/decorator/decorator.js | 2 +- test_files/decorator/decorator.tsickle.ts | 2 +- test_files/type/type.js | 4 ++-- test_files/type/type.tsickle.ts | 4 ++-- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/type-translator.ts b/src/type-translator.ts index 4574a34f9..c92bbfff2 100644 --- a/src/type-translator.ts +++ b/src/type-translator.ts @@ -323,12 +323,9 @@ export class TypeTranslator { break; default: let member = type.symbol.members[field]; - let isOptional = member.flags & ts.SymbolFlags.Optional; + // optional members are handled by the type including |undefined in a union type. let memberType = this.translate(this.typeChecker.getTypeOfSymbolAtLocation(member, this.node), true); - if (isOptional) { - memberType = `(${memberType}|undefined)`; - } fields.push(`${field}: ${memberType}`); } } diff --git a/test_files/decorator/decorator.js b/test_files/decorator/decorator.js index b8c07ca5e..0e7b9f81c 100644 --- a/test_files/decorator/decorator.js +++ b/test_files/decorator/decorator.js @@ -38,7 +38,7 @@ function DecoratorTest_tsickle_Closure_declarations() { /** @type {!Array} */ DecoratorTest.decorators; /** @nocollapse - @type {!Array<(null|{type: ?, decorators: ((undefined|!Array)|undefined)})>} */ + @type {!Array<(null|{type: ?, decorators: (undefined|!Array)})>} */ DecoratorTest.ctorParameters; /** @type {!Object>} */ DecoratorTest.propDecorators; diff --git a/test_files/decorator/decorator.tsickle.ts b/test_files/decorator/decorator.tsickle.ts index 09278227c..e92f3f94a 100644 --- a/test_files/decorator/decorator.tsickle.ts +++ b/test_files/decorator/decorator.tsickle.ts @@ -42,7 +42,7 @@ function DecoratorTest_tsickle_Closure_declarations() { /** @type {!Array} */ DecoratorTest.decorators; /** @nocollapse - @type {!Array<(null|{type: ?, decorators: ((undefined|!Array)|undefined)})>} */ + @type {!Array<(null|{type: ?, decorators: (undefined|!Array)})>} */ DecoratorTest.ctorParameters; /** @type {!Object>} */ DecoratorTest.propDecorators; diff --git a/test_files/type/type.js b/test_files/type/type.js index a38b207d1..4302fe54d 100644 --- a/test_files/type/type.js +++ b/test_files/type/type.js @@ -8,8 +8,8 @@ let /** @type {?} */ typeObject3; let /** @type {!Object} */ typeObjectEmpty; let /** @type {(string|boolean)} */ typeUnion = Math.random() > 0.5 ? false : ''; let /** @type {(string|boolean)} */ typeUnion2 = Math.random() > 0.5 ? false : ''; -let /** @type {{optional: ((undefined|boolean)|undefined)}} */ typeOptionalField = {}; -let /** @type {{optional: ((undefined|string|boolean)|undefined)}} */ typeOptionalUnion = {}; +let /** @type {{optional: (undefined|boolean)}} */ typeOptionalField = {}; +let /** @type {{optional: (undefined|string|boolean)}} */ typeOptionalUnion = {}; let /** @type {function(): void} */ typeFunc = function () { }; let /** @type {function(number, ?): string} */ typeFunc2 = function (a, b) { return ''; }; let /** @type {function(number, function(number): string): string} */ typeFunc3 = function (x, cb) { return ''; }; diff --git a/test_files/type/type.tsickle.ts b/test_files/type/type.tsickle.ts index 752cc3bc4..d6beacf34 100644 --- a/test_files/type/type.tsickle.ts +++ b/test_files/type/type.tsickle.ts @@ -19,8 +19,8 @@ let /** @type {!Object} */ typeObjectEmpty: {}; let /** @type {(string|boolean)} */ typeUnion: string|boolean = Math.random() > 0.5 ? false : ''; let /** @type {(string|boolean)} */ typeUnion2: (string|boolean) = Math.random() > 0.5 ? false : ''; -let /** @type {{optional: ((undefined|boolean)|undefined)}} */ typeOptionalField: {optional?: boolean} = {}; -let /** @type {{optional: ((undefined|string|boolean)|undefined)}} */ typeOptionalUnion: {optional?: string|boolean} = {}; +let /** @type {{optional: (undefined|boolean)}} */ typeOptionalField: {optional?: boolean} = {}; +let /** @type {{optional: (undefined|string|boolean)}} */ typeOptionalUnion: {optional?: string|boolean} = {}; let /** @type {function(): void} */ typeFunc: () => void = function() {}; let /** @type {function(number, ?): string} */ typeFunc2: (a: number, b: any) => string = function(a, b) { return ''; };