From 6abd5a134005db8ed53dd56e3a33382690caaaef Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 9 Mar 2020 15:13:15 -0700 Subject: [PATCH] Ensure computed property names are always checked --- src/compiler/checker.ts | 9 ++ .../jsFileImportPreservedWhenUsed.js | 45 ++++++++++ .../jsFileImportPreservedWhenUsed.symbols | 82 +++++++++++++++++++ .../jsFileImportPreservedWhenUsed.types | 75 +++++++++++++++++ .../compiler/jsFileImportPreservedWhenUsed.ts | 31 +++++++ 5 files changed, 242 insertions(+) create mode 100644 tests/baselines/reference/jsFileImportPreservedWhenUsed.js create mode 100644 tests/baselines/reference/jsFileImportPreservedWhenUsed.symbols create mode 100644 tests/baselines/reference/jsFileImportPreservedWhenUsed.types create mode 100644 tests/cases/compiler/jsFileImportPreservedWhenUsed.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 645bf049f4e12..4980489efeec1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22576,6 +22576,15 @@ namespace ts { let hasComputedNumberProperty = false; propertiesTable = createSymbolTable(); + // Spreads may cause an early bail; ensure computed names are always checked (this is cached) + // As otherwise they may not be checked until exports for the type at this position are retrieved, + // which may never occur. + for (const elem of node.properties) { + if (elem.name && isComputedPropertyName(elem.name) && !isWellKnownSymbolSyntactically(elem.name)) { + checkComputedPropertyName(elem.name); + } + } + let offset = 0; for (let i = 0; i < node.properties.length; i++) { const memberDecl = node.properties[i]; diff --git a/tests/baselines/reference/jsFileImportPreservedWhenUsed.js b/tests/baselines/reference/jsFileImportPreservedWhenUsed.js new file mode 100644 index 0000000000000..5f4a6d5bb48e6 --- /dev/null +++ b/tests/baselines/reference/jsFileImportPreservedWhenUsed.js @@ -0,0 +1,45 @@ +//// [tests/cases/compiler/jsFileImportPreservedWhenUsed.ts] //// + +//// [dash.d.ts] +type ObjectIterator = (value: TObject[keyof TObject], key: string, collection: TObject) => TResult; + +interface LoDashStatic { + mapValues(obj: T | null | undefined, callback: ObjectIterator): { [P in keyof T]: TResult }; +} +declare const _: LoDashStatic; +export = _; +//// [Consts.ts] +export const INDEX_FIELD = '__INDEX'; +//// [index.js] +import * as _ from './dash'; +import { INDEX_FIELD } from './Consts'; + +export class Test { + /** + * @param {object} obj + * @param {object} vm + */ + test(obj, vm) { + let index = 0; + vm.objects = _.mapValues( + obj, + object => ({ ...object, [INDEX_FIELD]: index++ }), + ); + } +} + +//// [Consts.js] +export const INDEX_FIELD = '__INDEX'; +//// [index.js] +import * as _ from './dash'; +import { INDEX_FIELD } from './Consts'; +export class Test { + /** + * @param {object} obj + * @param {object} vm + */ + test(obj, vm) { + let index = 0; + vm.objects = _.mapValues(obj, object => ({ ...object, [INDEX_FIELD]: index++ })); + } +} diff --git a/tests/baselines/reference/jsFileImportPreservedWhenUsed.symbols b/tests/baselines/reference/jsFileImportPreservedWhenUsed.symbols new file mode 100644 index 0000000000000..e98ec240cb219 --- /dev/null +++ b/tests/baselines/reference/jsFileImportPreservedWhenUsed.symbols @@ -0,0 +1,82 @@ +=== tests/cases/compiler/dash.d.ts === +type ObjectIterator = (value: TObject[keyof TObject], key: string, collection: TObject) => TResult; +>ObjectIterator : Symbol(ObjectIterator, Decl(dash.d.ts, 0, 0)) +>TObject : Symbol(TObject, Decl(dash.d.ts, 0, 20)) +>TResult : Symbol(TResult, Decl(dash.d.ts, 0, 28)) +>value : Symbol(value, Decl(dash.d.ts, 0, 41)) +>TObject : Symbol(TObject, Decl(dash.d.ts, 0, 20)) +>TObject : Symbol(TObject, Decl(dash.d.ts, 0, 20)) +>key : Symbol(key, Decl(dash.d.ts, 0, 71)) +>collection : Symbol(collection, Decl(dash.d.ts, 0, 84)) +>TObject : Symbol(TObject, Decl(dash.d.ts, 0, 20)) +>TResult : Symbol(TResult, Decl(dash.d.ts, 0, 28)) + +interface LoDashStatic { +>LoDashStatic : Symbol(LoDashStatic, Decl(dash.d.ts, 0, 117)) + + mapValues(obj: T | null | undefined, callback: ObjectIterator): { [P in keyof T]: TResult }; +>mapValues : Symbol(LoDashStatic.mapValues, Decl(dash.d.ts, 2, 24)) +>T : Symbol(T, Decl(dash.d.ts, 3, 14)) +>TResult : Symbol(TResult, Decl(dash.d.ts, 3, 31)) +>obj : Symbol(obj, Decl(dash.d.ts, 3, 41)) +>T : Symbol(T, Decl(dash.d.ts, 3, 14)) +>callback : Symbol(callback, Decl(dash.d.ts, 3, 67)) +>ObjectIterator : Symbol(ObjectIterator, Decl(dash.d.ts, 0, 0)) +>T : Symbol(T, Decl(dash.d.ts, 3, 14)) +>TResult : Symbol(TResult, Decl(dash.d.ts, 3, 31)) +>P : Symbol(P, Decl(dash.d.ts, 3, 110)) +>T : Symbol(T, Decl(dash.d.ts, 3, 14)) +>TResult : Symbol(TResult, Decl(dash.d.ts, 3, 31)) +} +declare const _: LoDashStatic; +>_ : Symbol(_, Decl(dash.d.ts, 5, 13)) +>LoDashStatic : Symbol(LoDashStatic, Decl(dash.d.ts, 0, 117)) + +export = _; +>_ : Symbol(_, Decl(dash.d.ts, 5, 13)) + +=== tests/cases/compiler/Consts.ts === +export const INDEX_FIELD = '__INDEX'; +>INDEX_FIELD : Symbol(INDEX_FIELD, Decl(Consts.ts, 0, 12)) + +=== tests/cases/compiler/index.js === +import * as _ from './dash'; +>_ : Symbol(_, Decl(index.js, 0, 6)) + +import { INDEX_FIELD } from './Consts'; +>INDEX_FIELD : Symbol(INDEX_FIELD, Decl(index.js, 1, 8)) + +export class Test { +>Test : Symbol(Test, Decl(index.js, 1, 39)) + + /** + * @param {object} obj + * @param {object} vm + */ + test(obj, vm) { +>test : Symbol(Test.test, Decl(index.js, 3, 19)) +>obj : Symbol(obj, Decl(index.js, 8, 9)) +>vm : Symbol(vm, Decl(index.js, 8, 13)) + + let index = 0; +>index : Symbol(index, Decl(index.js, 9, 11)) + + vm.objects = _.mapValues( +>vm : Symbol(vm, Decl(index.js, 8, 13)) +>_.mapValues : Symbol(LoDashStatic.mapValues, Decl(dash.d.ts, 2, 24)) +>_ : Symbol(_, Decl(index.js, 0, 6)) +>mapValues : Symbol(LoDashStatic.mapValues, Decl(dash.d.ts, 2, 24)) + + obj, +>obj : Symbol(obj, Decl(index.js, 8, 9)) + + object => ({ ...object, [INDEX_FIELD]: index++ }), +>object : Symbol(object, Decl(index.js, 11, 16)) +>object : Symbol(object, Decl(index.js, 11, 16)) +>[INDEX_FIELD] : Symbol([INDEX_FIELD], Decl(index.js, 12, 35)) +>INDEX_FIELD : Symbol(INDEX_FIELD, Decl(index.js, 1, 8)) +>index : Symbol(index, Decl(index.js, 9, 11)) + + ); + } +} diff --git a/tests/baselines/reference/jsFileImportPreservedWhenUsed.types b/tests/baselines/reference/jsFileImportPreservedWhenUsed.types new file mode 100644 index 0000000000000..bea5199a6af33 --- /dev/null +++ b/tests/baselines/reference/jsFileImportPreservedWhenUsed.types @@ -0,0 +1,75 @@ +=== tests/cases/compiler/dash.d.ts === +type ObjectIterator = (value: TObject[keyof TObject], key: string, collection: TObject) => TResult; +>ObjectIterator : ObjectIterator +>value : TObject[keyof TObject] +>key : string +>collection : TObject + +interface LoDashStatic { + mapValues(obj: T | null | undefined, callback: ObjectIterator): { [P in keyof T]: TResult }; +>mapValues : (obj: T | null | undefined, callback: ObjectIterator) => { [P in keyof T]: TResult; } +>obj : T | null | undefined +>null : null +>callback : ObjectIterator +} +declare const _: LoDashStatic; +>_ : LoDashStatic + +export = _; +>_ : LoDashStatic + +=== tests/cases/compiler/Consts.ts === +export const INDEX_FIELD = '__INDEX'; +>INDEX_FIELD : "__INDEX" +>'__INDEX' : "__INDEX" + +=== tests/cases/compiler/index.js === +import * as _ from './dash'; +>_ : LoDashStatic + +import { INDEX_FIELD } from './Consts'; +>INDEX_FIELD : "__INDEX" + +export class Test { +>Test : Test + + /** + * @param {object} obj + * @param {object} vm + */ + test(obj, vm) { +>test : (obj: object, vm: object) => void +>obj : object +>vm : object + + let index = 0; +>index : number +>0 : 0 + + vm.objects = _.mapValues( +>vm.objects = _.mapValues( obj, object => ({ ...object, [INDEX_FIELD]: index++ }), ) : object +>vm.objects : error +>vm : object +>objects : any +>_.mapValues( obj, object => ({ ...object, [INDEX_FIELD]: index++ }), ) : object +>_.mapValues : (obj: T | null | undefined, callback: (value: T[keyof T], key: string, collection: T) => TResult) => { [P in keyof T]: TResult; } +>_ : LoDashStatic +>mapValues : (obj: T | null | undefined, callback: (value: T[keyof T], key: string, collection: T) => TResult) => { [P in keyof T]: TResult; } + + obj, +>obj : object + + object => ({ ...object, [INDEX_FIELD]: index++ }), +>object => ({ ...object, [INDEX_FIELD]: index++ }) : (object: never) => any +>object : never +>({ ...object, [INDEX_FIELD]: index++ }) : error +>{ ...object, [INDEX_FIELD]: index++ } : error +>object : never +>[INDEX_FIELD] : number +>INDEX_FIELD : "__INDEX" +>index++ : number +>index : number + + ); + } +} diff --git a/tests/cases/compiler/jsFileImportPreservedWhenUsed.ts b/tests/cases/compiler/jsFileImportPreservedWhenUsed.ts new file mode 100644 index 0000000000000..acc992216a2ff --- /dev/null +++ b/tests/cases/compiler/jsFileImportPreservedWhenUsed.ts @@ -0,0 +1,31 @@ +// @target: esnext +// @allowJs: true +// @strict: true +// @outDir: ./out +// @filename: dash.d.ts +type ObjectIterator = (value: TObject[keyof TObject], key: string, collection: TObject) => TResult; + +interface LoDashStatic { + mapValues(obj: T | null | undefined, callback: ObjectIterator): { [P in keyof T]: TResult }; +} +declare const _: LoDashStatic; +export = _; +// @filename: Consts.ts +export const INDEX_FIELD = '__INDEX'; +// @filename: index.js +import * as _ from './dash'; +import { INDEX_FIELD } from './Consts'; + +export class Test { + /** + * @param {object} obj + * @param {object} vm + */ + test(obj, vm) { + let index = 0; + vm.objects = _.mapValues( + obj, + object => ({ ...object, [INDEX_FIELD]: index++ }), + ); + } +} \ No newline at end of file