From f07077c7ffdae3d81e79cedf7104f6ee11ec74e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 24 Aug 2023 17:35:48 +0200 Subject: [PATCH 01/12] Fixed a redundant used before defined error (#55283) --- src/compiler/checker.ts | 11 ++-- ...sedBeforeDefinedErrorInTypeContext.symbols | 50 +++++++++++++++++ ...oUsedBeforeDefinedErrorInTypeContext.types | 54 +++++++++++++++++++ .../noUsedBeforeDefinedErrorInTypeContext.ts | 23 ++++++++ 4 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.symbols create mode 100644 tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.types create mode 100644 tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8e69c07877f09..fd479090c5372 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2773,6 +2773,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return sourceFiles.indexOf(declarationFile) <= sourceFiles.indexOf(useFile); } + // deferred usage in a type context is always OK regardless of the usage position: + if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || isInAmbientOrTypeNode(usage)) { + return true; + } + if (declaration.pos <= usage.pos && !(isPropertyDeclaration(declaration) && isThisProperty(usage.parent) && !declaration.initializer && !declaration.exclamationToken)) { // declaration is before usage if (declaration.kind === SyntaxKind.BindingElement) { @@ -2813,9 +2818,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // (except when emitStandardClassFields: true and the reference is to a parameter property) // 4. inside a static property initializer, a reference to a static method in the same class // 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ) - // or if usage is in a type context: - // 1. inside a type query (typeof in type position) - // 2. inside a jsdoc comment if (usage.parent.kind === SyntaxKind.ExportSpecifier || (usage.parent.kind === SyntaxKind.ExportAssignment && (usage.parent as ExportAssignment).isExportEquals)) { // export specifiers do not use the variable, they only make it available for use return true; @@ -2825,9 +2827,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return true; } - if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || isInAmbientOrTypeNode(usage)) { - return true; - } if (isUsedInFunctionOrInstanceProperty(usage, declaration)) { if ( emitStandardClassFields diff --git a/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.symbols b/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.symbols new file mode 100644 index 0000000000000..2ac6072a46bf3 --- /dev/null +++ b/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.symbols @@ -0,0 +1,50 @@ +//// [tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts] //// + +=== noUsedBeforeDefinedErrorInTypeContext.ts === +// https://github.com/microsoft/TypeScript/issues/8775 + +interface IThing { +>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0)) +>T : Symbol(T, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 2, 17)) + + owner: T; +>owner : Symbol(IThing.owner, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 2, 21)) +>T : Symbol(T, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 2, 17)) +} + +var foo = { +>foo : Symbol(foo, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 6, 3)) + + one: {} as IThing, +>one : Symbol(one, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 6, 11)) +>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0)) +>foo : Symbol(foo, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 6, 3)) +} + +let baz = { +>baz : Symbol(baz, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 10, 3)) + + two: {} as IThing, +>two : Symbol(two, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 10, 11)) +>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0)) +>bar : Symbol(bar, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 14, 3)) +} + +let bar = { +>bar : Symbol(bar, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 14, 3)) + + three: {} as IThing, +>three : Symbol(three, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 14, 11)) +>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0)) +>bar : Symbol(bar, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 14, 3)) +} + +const qwe = { +>qwe : Symbol(qwe, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 18, 5)) + + four: {} as IThing, +>four : Symbol(four, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 18, 13)) +>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0)) +>qwe : Symbol(qwe, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 18, 5)) +} + diff --git a/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.types b/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.types new file mode 100644 index 0000000000000..247f743b916ef --- /dev/null +++ b/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.types @@ -0,0 +1,54 @@ +//// [tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts] //// + +=== noUsedBeforeDefinedErrorInTypeContext.ts === +// https://github.com/microsoft/TypeScript/issues/8775 + +interface IThing { + owner: T; +>owner : T +} + +var foo = { +>foo : any +>{ one: {} as IThing,} : { one: IThing; } + + one: {} as IThing, +>one : IThing +>{} as IThing : IThing +>{} : {} +>foo : any +} + +let baz = { +>baz : { two: IThing; } +>{ two: {} as IThing,} : { two: IThing; } + + two: {} as IThing, +>two : IThing +>{} as IThing : IThing +>{} : {} +>bar : any +} + +let bar = { +>bar : any +>{ three: {} as IThing,} : { three: IThing; } + + three: {} as IThing, +>three : IThing +>{} as IThing : IThing +>{} : {} +>bar : any +} + +const qwe = { +>qwe : any +>{ four: {} as IThing,} : { four: IThing; } + + four: {} as IThing, +>four : IThing +>{} as IThing : IThing +>{} : {} +>qwe : any +} + diff --git a/tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts b/tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts new file mode 100644 index 0000000000000..7715278f189c0 --- /dev/null +++ b/tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts @@ -0,0 +1,23 @@ +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/8775 + +interface IThing { + owner: T; +} + +var foo = { + one: {} as IThing, +} + +let baz = { + two: {} as IThing, +} + +let bar = { + three: {} as IThing, +} + +const qwe = { + four: {} as IThing, +} From fecbae5d2e041fe08c7ed723e74b696e4fe75989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 24 Aug 2023 18:45:58 +0200 Subject: [PATCH 02/12] Fixed a regression in serializing setters with non-function declarations in JS files (#55492) --- src/compiler/checker.ts | 4 +- .../reference/jsDeclarationsGetterSetter.js | 128 +++++++++++++ .../jsDeclarationsGetterSetter.symbols | 155 +++++++++++++++ .../jsDeclarationsGetterSetter.types | 179 ++++++++++++++++++ .../jsDeclarationsGetterSetter.ts | 57 ++++++ 5 files changed, 521 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fd479090c5372..bc09440dd7e4c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9803,8 +9803,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } }); - Debug.assert(setter && isFunctionLikeDeclaration(setter)); - const paramSymbol: Symbol | undefined = getSignatureFromDeclaration(setter).parameters[0]; + Debug.assert(!!setter); + const paramSymbol = isFunctionLikeDeclaration(setter) ? getSignatureFromDeclaration(setter).parameters[0] : undefined; result.push(setTextRange( factory.createSetAccessorDeclaration( diff --git a/tests/baselines/reference/jsDeclarationsGetterSetter.js b/tests/baselines/reference/jsDeclarationsGetterSetter.js index 31941471c0399..90a1a35f8a873 100644 --- a/tests/baselines/reference/jsDeclarationsGetterSetter.js +++ b/tests/baselines/reference/jsDeclarationsGetterSetter.js @@ -61,6 +61,63 @@ export class H {} Object.defineProperty(H.prototype, "x", { set() {} }); + + +export class I {} +Object.defineProperty(I.prototype, "x", { + /** + * @param {number} v + */ + set: (v) => {} +}); + +/** + * @param {number} v + */ +const jSetter = (v) => {} +export class J {} +Object.defineProperty(J.prototype, "x", { + set: jSetter +}); + +/** + * @param {number} v + */ +const kSetter1 = (v) => {} +/** + * @param {number} v + */ +const kSetter2 = (v) => {} +export class K {} +Object.defineProperty(K.prototype, "x", { + set: Math.random() ? kSetter1 : kSetter2 +}); + +/** + * @param {number} v + */ +const lSetter1 = (v) => {} +/** + * @param {string} v + */ +const lSetter2 = (v) => {} +export class L {} +Object.defineProperty(L.prototype, "x", { + set: Math.random() ? lSetter1 : lSetter2 +}); + +/** + * @param {number | boolean} v + */ +const mSetter1 = (v) => {} +/** + * @param {string | boolean} v + */ +const mSetter2 = (v) => {} +export class M {} +Object.defineProperty(M.prototype, "x", { + set: Math.random() ? mSetter1 : mSetter2 +}); //// [index.js] @@ -122,6 +179,62 @@ export class H { Object.defineProperty(H.prototype, "x", { set() { } }); +export class I { +} +Object.defineProperty(I.prototype, "x", { + /** + * @param {number} v + */ + set: (v) => { } +}); +/** + * @param {number} v + */ +const jSetter = (v) => { }; +export class J { +} +Object.defineProperty(J.prototype, "x", { + set: jSetter +}); +/** + * @param {number} v + */ +const kSetter1 = (v) => { }; +/** + * @param {number} v + */ +const kSetter2 = (v) => { }; +export class K { +} +Object.defineProperty(K.prototype, "x", { + set: Math.random() ? kSetter1 : kSetter2 +}); +/** + * @param {number} v + */ +const lSetter1 = (v) => { }; +/** + * @param {string} v + */ +const lSetter2 = (v) => { }; +export class L { +} +Object.defineProperty(L.prototype, "x", { + set: Math.random() ? lSetter1 : lSetter2 +}); +/** + * @param {number | boolean} v + */ +const mSetter1 = (v) => { }; +/** + * @param {string | boolean} v + */ +const mSetter2 = (v) => { }; +export class M { +} +Object.defineProperty(M.prototype, "x", { + set: Math.random() ? mSetter1 : mSetter2 +}); //// [index.d.ts] @@ -154,3 +267,18 @@ export class G { export class H { set x(value: any); } +export class I { + set x(value: number); +} +export class J { + set x(value: number); +} +export class K { + set x(value: number); +} +export class L { + set x(value: any); +} +export class M { + set x(value: any); +} diff --git a/tests/baselines/reference/jsDeclarationsGetterSetter.symbols b/tests/baselines/reference/jsDeclarationsGetterSetter.symbols index d0d98a8158cad..90a1d37bbbb22 100644 --- a/tests/baselines/reference/jsDeclarationsGetterSetter.symbols +++ b/tests/baselines/reference/jsDeclarationsGetterSetter.symbols @@ -141,3 +141,158 @@ Object.defineProperty(H.prototype, "x", { }); + +export class I {} +>I : Symbol(I, Decl(index.js, 59, 3)) + +Object.defineProperty(I.prototype, "x", { +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>I.prototype : Symbol(I.prototype) +>I : Symbol(I, Decl(index.js, 59, 3)) +>prototype : Symbol(I.prototype) +>"x" : Symbol(I.x, Decl(index.js, 62, 17)) + + /** + * @param {number} v + */ + set: (v) => {} +>set : Symbol(set, Decl(index.js, 63, 41)) +>v : Symbol(v, Decl(index.js, 67, 10)) + +}); + +/** + * @param {number} v + */ +const jSetter = (v) => {} +>jSetter : Symbol(jSetter, Decl(index.js, 73, 5)) +>v : Symbol(v, Decl(index.js, 73, 17)) + +export class J {} +>J : Symbol(J, Decl(index.js, 73, 25)) + +Object.defineProperty(J.prototype, "x", { +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>J.prototype : Symbol(J.prototype) +>J : Symbol(J, Decl(index.js, 73, 25)) +>prototype : Symbol(J.prototype) +>"x" : Symbol(J.x, Decl(index.js, 74, 17)) + + set: jSetter +>set : Symbol(set, Decl(index.js, 75, 41)) +>jSetter : Symbol(jSetter, Decl(index.js, 73, 5)) + +}); + +/** + * @param {number} v + */ +const kSetter1 = (v) => {} +>kSetter1 : Symbol(kSetter1, Decl(index.js, 82, 5)) +>v : Symbol(v, Decl(index.js, 82, 18)) + +/** + * @param {number} v + */ +const kSetter2 = (v) => {} +>kSetter2 : Symbol(kSetter2, Decl(index.js, 86, 5)) +>v : Symbol(v, Decl(index.js, 86, 18)) + +export class K {} +>K : Symbol(K, Decl(index.js, 86, 26)) + +Object.defineProperty(K.prototype, "x", { +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>K.prototype : Symbol(K.prototype) +>K : Symbol(K, Decl(index.js, 86, 26)) +>prototype : Symbol(K.prototype) +>"x" : Symbol(K.x, Decl(index.js, 87, 17)) + + set: Math.random() ? kSetter1 : kSetter2 +>set : Symbol(set, Decl(index.js, 88, 41)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>kSetter1 : Symbol(kSetter1, Decl(index.js, 82, 5)) +>kSetter2 : Symbol(kSetter2, Decl(index.js, 86, 5)) + +}); + +/** + * @param {number} v + */ +const lSetter1 = (v) => {} +>lSetter1 : Symbol(lSetter1, Decl(index.js, 95, 5)) +>v : Symbol(v, Decl(index.js, 95, 18)) + +/** + * @param {string} v + */ +const lSetter2 = (v) => {} +>lSetter2 : Symbol(lSetter2, Decl(index.js, 99, 5)) +>v : Symbol(v, Decl(index.js, 99, 18)) + +export class L {} +>L : Symbol(L, Decl(index.js, 99, 26)) + +Object.defineProperty(L.prototype, "x", { +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>L.prototype : Symbol(L.prototype) +>L : Symbol(L, Decl(index.js, 99, 26)) +>prototype : Symbol(L.prototype) +>"x" : Symbol(L.x, Decl(index.js, 100, 17)) + + set: Math.random() ? lSetter1 : lSetter2 +>set : Symbol(set, Decl(index.js, 101, 41)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>lSetter1 : Symbol(lSetter1, Decl(index.js, 95, 5)) +>lSetter2 : Symbol(lSetter2, Decl(index.js, 99, 5)) + +}); + +/** + * @param {number | boolean} v + */ +const mSetter1 = (v) => {} +>mSetter1 : Symbol(mSetter1, Decl(index.js, 108, 5)) +>v : Symbol(v, Decl(index.js, 108, 18)) + +/** + * @param {string | boolean} v + */ +const mSetter2 = (v) => {} +>mSetter2 : Symbol(mSetter2, Decl(index.js, 112, 5)) +>v : Symbol(v, Decl(index.js, 112, 18)) + +export class M {} +>M : Symbol(M, Decl(index.js, 112, 26)) + +Object.defineProperty(M.prototype, "x", { +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>M.prototype : Symbol(M.prototype) +>M : Symbol(M, Decl(index.js, 112, 26)) +>prototype : Symbol(M.prototype) +>"x" : Symbol(M.x, Decl(index.js, 113, 17)) + + set: Math.random() ? mSetter1 : mSetter2 +>set : Symbol(set, Decl(index.js, 114, 41)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>mSetter1 : Symbol(mSetter1, Decl(index.js, 108, 5)) +>mSetter2 : Symbol(mSetter2, Decl(index.js, 112, 5)) + +}); + diff --git a/tests/baselines/reference/jsDeclarationsGetterSetter.types b/tests/baselines/reference/jsDeclarationsGetterSetter.types index 3b4ce59488509..d0746a23a3ea0 100644 --- a/tests/baselines/reference/jsDeclarationsGetterSetter.types +++ b/tests/baselines/reference/jsDeclarationsGetterSetter.types @@ -156,3 +156,182 @@ Object.defineProperty(H.prototype, "x", { }); + +export class I {} +>I : I + +Object.defineProperty(I.prototype, "x", { +>Object.defineProperty(I.prototype, "x", { /** * @param {number} v */ set: (v) => {}}) : I +>Object.defineProperty : (o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType) => T +>Object : ObjectConstructor +>defineProperty : (o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType) => T +>I.prototype : I +>I : typeof I +>prototype : I +>"x" : "x" +>{ /** * @param {number} v */ set: (v) => {}} : { set: (v: number) => void; } + + /** + * @param {number} v + */ + set: (v) => {} +>set : (v: number) => void +>(v) => {} : (v: number) => void +>v : number + +}); + +/** + * @param {number} v + */ +const jSetter = (v) => {} +>jSetter : (v: number) => void +>(v) => {} : (v: number) => void +>v : number + +export class J {} +>J : J + +Object.defineProperty(J.prototype, "x", { +>Object.defineProperty(J.prototype, "x", { set: jSetter}) : J +>Object.defineProperty : (o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType) => T +>Object : ObjectConstructor +>defineProperty : (o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType) => T +>J.prototype : J +>J : typeof J +>prototype : J +>"x" : "x" +>{ set: jSetter} : { set: (v: number) => void; } + + set: jSetter +>set : (v: number) => void +>jSetter : (v: number) => void + +}); + +/** + * @param {number} v + */ +const kSetter1 = (v) => {} +>kSetter1 : (v: number) => void +>(v) => {} : (v: number) => void +>v : number + +/** + * @param {number} v + */ +const kSetter2 = (v) => {} +>kSetter2 : (v: number) => void +>(v) => {} : (v: number) => void +>v : number + +export class K {} +>K : K + +Object.defineProperty(K.prototype, "x", { +>Object.defineProperty(K.prototype, "x", { set: Math.random() ? kSetter1 : kSetter2}) : K +>Object.defineProperty : (o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType) => T +>Object : ObjectConstructor +>defineProperty : (o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType) => T +>K.prototype : K +>K : typeof K +>prototype : K +>"x" : "x" +>{ set: Math.random() ? kSetter1 : kSetter2} : { set: (v: number) => void; } + + set: Math.random() ? kSetter1 : kSetter2 +>set : (v: number) => void +>Math.random() ? kSetter1 : kSetter2 : (v: number) => void +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>kSetter1 : (v: number) => void +>kSetter2 : (v: number) => void + +}); + +/** + * @param {number} v + */ +const lSetter1 = (v) => {} +>lSetter1 : (v: number) => void +>(v) => {} : (v: number) => void +>v : number + +/** + * @param {string} v + */ +const lSetter2 = (v) => {} +>lSetter2 : (v: string) => void +>(v) => {} : (v: string) => void +>v : string + +export class L {} +>L : L + +Object.defineProperty(L.prototype, "x", { +>Object.defineProperty(L.prototype, "x", { set: Math.random() ? lSetter1 : lSetter2}) : L +>Object.defineProperty : (o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType) => T +>Object : ObjectConstructor +>defineProperty : (o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType) => T +>L.prototype : L +>L : typeof L +>prototype : L +>"x" : "x" +>{ set: Math.random() ? lSetter1 : lSetter2} : { set: ((v: number) => void) | ((v: string) => void); } + + set: Math.random() ? lSetter1 : lSetter2 +>set : ((v: number) => void) | ((v: string) => void) +>Math.random() ? lSetter1 : lSetter2 : ((v: number) => void) | ((v: string) => void) +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>lSetter1 : (v: number) => void +>lSetter2 : (v: string) => void + +}); + +/** + * @param {number | boolean} v + */ +const mSetter1 = (v) => {} +>mSetter1 : (v: number | boolean) => void +>(v) => {} : (v: number | boolean) => void +>v : number | boolean + +/** + * @param {string | boolean} v + */ +const mSetter2 = (v) => {} +>mSetter2 : (v: string | boolean) => void +>(v) => {} : (v: string | boolean) => void +>v : string | boolean + +export class M {} +>M : M + +Object.defineProperty(M.prototype, "x", { +>Object.defineProperty(M.prototype, "x", { set: Math.random() ? mSetter1 : mSetter2}) : M +>Object.defineProperty : (o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType) => T +>Object : ObjectConstructor +>defineProperty : (o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType) => T +>M.prototype : M +>M : typeof M +>prototype : M +>"x" : "x" +>{ set: Math.random() ? mSetter1 : mSetter2} : { set: ((v: number | boolean) => void) | ((v: string | boolean) => void); } + + set: Math.random() ? mSetter1 : mSetter2 +>set : ((v: number | boolean) => void) | ((v: string | boolean) => void) +>Math.random() ? mSetter1 : mSetter2 : ((v: number | boolean) => void) | ((v: string | boolean) => void) +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>mSetter1 : (v: number | boolean) => void +>mSetter2 : (v: string | boolean) => void + +}); + diff --git a/tests/cases/conformance/jsdoc/declarations/jsDeclarationsGetterSetter.ts b/tests/cases/conformance/jsdoc/declarations/jsDeclarationsGetterSetter.ts index f42dcf64573f2..aacd499707485 100644 --- a/tests/cases/conformance/jsdoc/declarations/jsDeclarationsGetterSetter.ts +++ b/tests/cases/conformance/jsdoc/declarations/jsDeclarationsGetterSetter.ts @@ -64,3 +64,60 @@ export class H {} Object.defineProperty(H.prototype, "x", { set() {} }); + + +export class I {} +Object.defineProperty(I.prototype, "x", { + /** + * @param {number} v + */ + set: (v) => {} +}); + +/** + * @param {number} v + */ +const jSetter = (v) => {} +export class J {} +Object.defineProperty(J.prototype, "x", { + set: jSetter +}); + +/** + * @param {number} v + */ +const kSetter1 = (v) => {} +/** + * @param {number} v + */ +const kSetter2 = (v) => {} +export class K {} +Object.defineProperty(K.prototype, "x", { + set: Math.random() ? kSetter1 : kSetter2 +}); + +/** + * @param {number} v + */ +const lSetter1 = (v) => {} +/** + * @param {string} v + */ +const lSetter2 = (v) => {} +export class L {} +Object.defineProperty(L.prototype, "x", { + set: Math.random() ? lSetter1 : lSetter2 +}); + +/** + * @param {number | boolean} v + */ +const mSetter1 = (v) => {} +/** + * @param {string | boolean} v + */ +const mSetter2 = (v) => {} +export class M {} +Object.defineProperty(M.prototype, "x", { + set: Math.random() ? mSetter1 : mSetter2 +}); From 98d7e0b93646374ea226d7d18715b7e17a6cdea1 Mon Sep 17 00:00:00 2001 From: Shinichi Katayama Date: Thu, 24 Aug 2023 09:57:22 -0700 Subject: [PATCH 03/12] Fix double error on invalid delete of readonly property (#55449) --- src/compiler/checker.ts | 4 +++- .../deleteReadonlyInStrictNullChecks.errors.txt | 10 ++++++++++ .../reference/deleteReadonlyInStrictNullChecks.js | 15 +++++++++++++++ .../deleteReadonlyInStrictNullChecks.symbols | 15 +++++++++++++++ .../deleteReadonlyInStrictNullChecks.types | 15 +++++++++++++++ .../compiler/deleteReadonlyInStrictNullChecks.ts | 5 +++++ 6 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/deleteReadonlyInStrictNullChecks.errors.txt create mode 100644 tests/baselines/reference/deleteReadonlyInStrictNullChecks.js create mode 100644 tests/baselines/reference/deleteReadonlyInStrictNullChecks.symbols create mode 100644 tests/baselines/reference/deleteReadonlyInStrictNullChecks.types create mode 100644 tests/cases/compiler/deleteReadonlyInStrictNullChecks.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc09440dd7e4c..3eb6ce3681e5f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -36713,7 +36713,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isReadonlySymbol(symbol)) { error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property); } - checkDeleteExpressionMustBeOptional(expr, symbol); + else { + checkDeleteExpressionMustBeOptional(expr, symbol); + } } return booleanType; } diff --git a/tests/baselines/reference/deleteReadonlyInStrictNullChecks.errors.txt b/tests/baselines/reference/deleteReadonlyInStrictNullChecks.errors.txt new file mode 100644 index 0000000000000..55fef2ff5a609 --- /dev/null +++ b/tests/baselines/reference/deleteReadonlyInStrictNullChecks.errors.txt @@ -0,0 +1,10 @@ +deleteReadonlyInStrictNullChecks.ts(3,8): error TS2704: The operand of a 'delete' operator cannot be a read-only property. + + +==== deleteReadonlyInStrictNullChecks.ts (1 errors) ==== + interface Function { readonly name: string; } + class Foo {} + delete Foo.name; + ~~~~~~~~ +!!! error TS2704: The operand of a 'delete' operator cannot be a read-only property. + \ No newline at end of file diff --git a/tests/baselines/reference/deleteReadonlyInStrictNullChecks.js b/tests/baselines/reference/deleteReadonlyInStrictNullChecks.js new file mode 100644 index 0000000000000..da2d57e18cb38 --- /dev/null +++ b/tests/baselines/reference/deleteReadonlyInStrictNullChecks.js @@ -0,0 +1,15 @@ +//// [tests/cases/compiler/deleteReadonlyInStrictNullChecks.ts] //// + +//// [deleteReadonlyInStrictNullChecks.ts] +interface Function { readonly name: string; } +class Foo {} +delete Foo.name; + + +//// [deleteReadonlyInStrictNullChecks.js] +var Foo = /** @class */ (function () { + function Foo() { + } + return Foo; +}()); +delete Foo.name; diff --git a/tests/baselines/reference/deleteReadonlyInStrictNullChecks.symbols b/tests/baselines/reference/deleteReadonlyInStrictNullChecks.symbols new file mode 100644 index 0000000000000..7cca9a38372de --- /dev/null +++ b/tests/baselines/reference/deleteReadonlyInStrictNullChecks.symbols @@ -0,0 +1,15 @@ +//// [tests/cases/compiler/deleteReadonlyInStrictNullChecks.ts] //// + +=== deleteReadonlyInStrictNullChecks.ts === +interface Function { readonly name: string; } +>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(deleteReadonlyInStrictNullChecks.ts, 0, 0)) +>name : Symbol(Function.name, Decl(deleteReadonlyInStrictNullChecks.ts, 0, 20)) + +class Foo {} +>Foo : Symbol(Foo, Decl(deleteReadonlyInStrictNullChecks.ts, 0, 45)) + +delete Foo.name; +>Foo.name : Symbol(Function.name, Decl(deleteReadonlyInStrictNullChecks.ts, 0, 20)) +>Foo : Symbol(Foo, Decl(deleteReadonlyInStrictNullChecks.ts, 0, 45)) +>name : Symbol(Function.name, Decl(deleteReadonlyInStrictNullChecks.ts, 0, 20)) + diff --git a/tests/baselines/reference/deleteReadonlyInStrictNullChecks.types b/tests/baselines/reference/deleteReadonlyInStrictNullChecks.types new file mode 100644 index 0000000000000..9bb089d7a4c71 --- /dev/null +++ b/tests/baselines/reference/deleteReadonlyInStrictNullChecks.types @@ -0,0 +1,15 @@ +//// [tests/cases/compiler/deleteReadonlyInStrictNullChecks.ts] //// + +=== deleteReadonlyInStrictNullChecks.ts === +interface Function { readonly name: string; } +>name : string + +class Foo {} +>Foo : Foo + +delete Foo.name; +>delete Foo.name : boolean +>Foo.name : string +>Foo : typeof Foo +>name : string + diff --git a/tests/cases/compiler/deleteReadonlyInStrictNullChecks.ts b/tests/cases/compiler/deleteReadonlyInStrictNullChecks.ts new file mode 100644 index 0000000000000..83faef91ea67f --- /dev/null +++ b/tests/cases/compiler/deleteReadonlyInStrictNullChecks.ts @@ -0,0 +1,5 @@ +// @strictNullChecks: true + +interface Function { readonly name: string; } +class Foo {} +delete Foo.name; From 91c0d7ff9b3e43de673ee9fe539754146387832a Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Thu, 24 Aug 2023 10:12:28 -0700 Subject: [PATCH 04/12] Avoid resolving objects in getTypeFacts when caller doesn't need that info (#55459) --- src/compiler/checker.ts | 76 +++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3eb6ce3681e5f..392883ae41fd7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10651,7 +10651,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { parentType = getNonNullableType(parentType); } // Filter `undefined` from the type we check against if the parent has an initializer and that initializer is not possibly `undefined` - else if (strictNullChecks && pattern.parent.initializer && !(getTypeFacts(getTypeOfInitializer(pattern.parent.initializer)) & TypeFacts.EQUndefined)) { + else if (strictNullChecks && pattern.parent.initializer && !(hasTypeFacts(getTypeOfInitializer(pattern.parent.initializer), TypeFacts.EQUndefined))) { parentType = getTypeWithFacts(parentType, TypeFacts.NEUndefined); } @@ -10710,7 +10710,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) { // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. - return strictNullChecks && !(getTypeFacts(checkDeclarationInitializer(declaration, CheckMode.Normal)) & TypeFacts.IsUndefined) ? getNonUndefinedType(type) : type; + return strictNullChecks && !(hasTypeFacts(checkDeclarationInitializer(declaration, CheckMode.Normal), TypeFacts.IsUndefined)) ? getNonUndefinedType(type) : type; } return widenTypeInferredFromInitializer(declaration, getUnionType([getNonUndefinedType(type), checkDeclarationInitializer(declaration, CheckMode.Normal)], UnionReduction.Subtype)); } @@ -20332,7 +20332,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const sourceSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(sourceType)); const targetSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(targetType)); const callbacks = sourceSig && targetSig && !getTypePredicateOfSignature(sourceSig) && !getTypePredicateOfSignature(targetSig) && - (getTypeFacts(sourceType) & TypeFacts.IsUndefinedOrNull) === (getTypeFacts(targetType) & TypeFacts.IsUndefinedOrNull); + getTypeFacts(sourceType, TypeFacts.IsUndefinedOrNull) === getTypeFacts(targetType, TypeFacts.IsUndefinedOrNull); let related = callbacks ? compareSignaturesRelated(targetSig, sourceSig, (checkMode & SignatureCheckMode.StrictArity) | (strictVariance ? SignatureCheckMode.StrictCallback : SignatureCheckMode.BivariantCallback), reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) : !(checkMode & SignatureCheckMode.Callback) && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors); @@ -23894,7 +23894,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function removeDefinitelyFalsyTypes(type: Type): Type { - return filterType(type, t => !!(getTypeFacts(t) & TypeFacts.Truthy)); + return filterType(type, t => hasTypeFacts(t, TypeFacts.Truthy)); } function extractDefinitelyFalsyTypes(type: Type): Type { @@ -26141,7 +26141,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { resolved.members.get("bind" as __String) && isTypeSubtypeOf(type, globalFunctionType)); } - function getTypeFacts(type: Type): TypeFacts { + function getTypeFacts(type: Type, mask: TypeFacts): TypeFacts { + return getTypeFactsWorker(type, mask) & mask; + } + + function hasTypeFacts(type: Type, mask: TypeFacts): boolean { + return getTypeFacts(type, mask) !== 0; + } + + function getTypeFactsWorker(type: Type, callerOnlyNeeds: TypeFacts): TypeFacts { if (type.flags & (TypeFlags.Intersection | TypeFlags.Instantiable)) { type = getBaseConstraintOfType(type) || unknownType; } @@ -26182,6 +26190,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { (type === falseType || type === regularFalseType) ? TypeFacts.FalseFacts : TypeFacts.TrueFacts; } if (flags & TypeFlags.Object) { + const possibleFacts = strictNullChecks + ? TypeFacts.EmptyObjectStrictFacts | TypeFacts.FunctionStrictFacts | TypeFacts.ObjectStrictFacts + : TypeFacts.EmptyObjectFacts | TypeFacts.FunctionFacts | TypeFacts.ObjectFacts; + + if ((callerOnlyNeeds & possibleFacts) === 0) { + // If the caller doesn't care about any of the facts that we could possibly produce, + // return zero so we can skip resolving members. + return 0; + } + return getObjectFlags(type) & ObjectFlags.Anonymous && isEmptyObjectType(type as ObjectType) ? strictNullChecks ? TypeFacts.EmptyObjectStrictFacts : TypeFacts.EmptyObjectFacts : isFunctionObjectType(type as ObjectType) ? @@ -26207,15 +26225,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return TypeFacts.None; } if (flags & TypeFlags.Union) { - return reduceLeft((type as UnionType).types, (facts, t) => facts | getTypeFacts(t), TypeFacts.None); + return reduceLeft((type as UnionType).types, (facts, t) => facts | getTypeFactsWorker(t, callerOnlyNeeds), TypeFacts.None); } if (flags & TypeFlags.Intersection) { - return getIntersectionTypeFacts(type as IntersectionType); + return getIntersectionTypeFacts(type as IntersectionType, callerOnlyNeeds); } return TypeFacts.UnknownFacts; } - function getIntersectionTypeFacts(type: IntersectionType): TypeFacts { + function getIntersectionTypeFacts(type: IntersectionType, callerOnlyNeeds: TypeFacts): TypeFacts { // When an intersection contains a primitive type we ignore object type constituents as they are // presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type. const ignoreObjects = maybeTypeOfKind(type, TypeFlags.Primitive); @@ -26225,7 +26243,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let andedFacts = TypeFacts.All; for (const t of type.types) { if (!(ignoreObjects && t.flags & TypeFlags.Object)) { - const f = getTypeFacts(t); + const f = getTypeFactsWorker(t, callerOnlyNeeds); oredFacts |= f; andedFacts &= f; } @@ -26234,7 +26252,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getTypeWithFacts(type: Type, include: TypeFacts) { - return filterType(type, t => (getTypeFacts(t) & include) !== 0); + return filterType(type, t => hasTypeFacts(t, include)); } // This function is similar to getTypeWithFacts, except that in strictNullChecks mode it replaces type @@ -26245,12 +26263,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (strictNullChecks) { switch (facts) { case TypeFacts.NEUndefined: - return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefined ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQNull && !maybeTypeOfKind(reduced, TypeFlags.Null) ? getUnionType([emptyObjectType, nullType]) : emptyObjectType]) : t); + return mapType(reduced, t => hasTypeFacts(t, TypeFacts.EQUndefined) ? getIntersectionType([t, hasTypeFacts(t, TypeFacts.EQNull) && !maybeTypeOfKind(reduced, TypeFlags.Null) ? getUnionType([emptyObjectType, nullType]) : emptyObjectType]) : t); case TypeFacts.NENull: - return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQNull ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQUndefined && !maybeTypeOfKind(reduced, TypeFlags.Undefined) ? getUnionType([emptyObjectType, undefinedType]) : emptyObjectType]) : t); + return mapType(reduced, t => hasTypeFacts(t, TypeFacts.EQNull) ? getIntersectionType([t, hasTypeFacts(t, TypeFacts.EQUndefined) && !maybeTypeOfKind(reduced, TypeFlags.Undefined) ? getUnionType([emptyObjectType, undefinedType]) : emptyObjectType]) : t); case TypeFacts.NEUndefinedOrNull: case TypeFacts.Truthy: - return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefinedOrNull ? getGlobalNonNullableTypeInstantiation(t) : t); + return mapType(reduced, t => hasTypeFacts(t, TypeFacts.EQUndefinedOrNull) ? getGlobalNonNullableTypeInstantiation(t) : t); } } return reduced; @@ -27818,14 +27836,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // the constituent based on its type facts. We use the strict subtype relation because it treats `object` // as a subtype of `{}`, and we need the type facts check because function types are subtypes of `object`, // but are classified as "function" according to `typeof`. - isTypeRelatedTo(t, impliedType, strictSubtypeRelation) ? getTypeFacts(t) & facts ? t : neverType : + isTypeRelatedTo(t, impliedType, strictSubtypeRelation) ? hasTypeFacts(t, facts) ? t : neverType : // We next check if the consituent is a supertype of the implied type. If so, we substitute the implied // type. This handles top types like `unknown` and `{}`, and supertypes like `{ toString(): string }`. isTypeSubtypeOf(impliedType, t) ? impliedType : // Neither the constituent nor the implied type is a subtype of the other, however their domains may still // overlap. For example, an unconstrained type parameter and type `string`. If the type facts indicate // possible overlap, we form an intersection. Otherwise, we eliminate the constituent. - getTypeFacts(t) & facts ? getIntersectionType([t, impliedType]) : + hasTypeFacts(t, facts) ? getIntersectionType([t, impliedType]) : neverType); } @@ -27840,7 +27858,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (hasDefaultClause) { // In the default clause we filter constituents down to those that are not-equal to all handled cases. const notEqualFacts = getNotEqualFactsFromTypeofSwitch(clauseStart, clauseEnd, witnesses); - return filterType(type, t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts); + return filterType(type, t => getTypeFacts(t, notEqualFacts) === notEqualFacts); } // In the non-default cause we create a union of the type narrowed by each of the listed cases. const clauseWitnesses = witnesses.slice(clauseStart, clauseEnd); @@ -28023,7 +28041,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if ( strictNullChecks && assumeTrue && optionalChainContainsReference(predicateArgument, reference) && - !(getTypeFacts(predicate.type) & TypeFacts.EQUndefined) + !(hasTypeFacts(predicate.type, TypeFacts.EQUndefined)) ) { type = getAdjustedTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } @@ -28184,7 +28202,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return true; } - const containsUndefined = !!(getTypeFacts(checkDeclarationInitializer(declaration, CheckMode.Normal)) & TypeFacts.IsUndefined); + const containsUndefined = !!(hasTypeFacts(checkDeclarationInitializer(declaration, CheckMode.Normal), TypeFacts.IsUndefined)); if (!popTypeResolution()) { reportCircularityError(declaration.symbol); @@ -28202,7 +28220,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const removeUndefined = strictNullChecks && declaration.kind === SyntaxKind.Parameter && declaration.initializer && - getTypeFacts(declaredType) & TypeFacts.IsUndefined && + hasTypeFacts(declaredType, TypeFacts.IsUndefined) && !parameterInitializerContainsUndefined(declaration); return removeUndefined ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType; @@ -31781,7 +31799,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isNullableType(type: Type) { - return !!(getTypeFacts(type) & TypeFacts.IsUndefinedOrNull); + return hasTypeFacts(type, TypeFacts.IsUndefinedOrNull); } function getNonNullableTypeIfNeeded(type: Type) { @@ -31845,7 +31863,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { error(node, Diagnostics.Object_is_of_type_unknown); return errorType; } - const facts = getTypeFacts(type); + const facts = getTypeFacts(type, TypeFacts.IsUndefinedOrNull); if (facts & TypeFacts.IsUndefinedOrNull) { reportError(node, facts); const t = getNonNullableType(type); @@ -36307,7 +36325,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return (TypeFacts.AllTypeofNE & notEqualFacts) === TypeFacts.AllTypeofNE; } // A missing not-equal flag indicates that the type wasn't handled by some case. - return !someType(operandConstraint, t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts); + return !someType(operandConstraint, t => getTypeFacts(t, notEqualFacts) === notEqualFacts); } const type = checkExpressionCached(node.expression); if (!isLiteralType(type)) { @@ -36725,7 +36743,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if ( strictNullChecks && !(type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Never)) && - !(exactOptionalPropertyTypes ? symbol.flags & SymbolFlags.Optional : getTypeFacts(type) & TypeFacts.IsUndefined) + !(exactOptionalPropertyTypes ? symbol.flags & SymbolFlags.Optional : hasTypeFacts(type, TypeFacts.IsUndefined)) ) { error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_optional); } @@ -36871,7 +36889,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getUnaryResultType(operandType); case SyntaxKind.ExclamationToken: checkTruthinessOfType(operandType, node.operand); - const facts = getTypeFacts(operandType) & (TypeFacts.Truthy | TypeFacts.Falsy); + const facts = getTypeFacts(operandType, TypeFacts.Truthy | TypeFacts.Falsy); return facts === TypeFacts.Truthy ? falseType : facts === TypeFacts.Falsy ? trueType : booleanType; @@ -37162,7 +37180,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // undefined from the final type. if ( strictNullChecks && - !(getTypeFacts(checkExpression(prop.objectAssignmentInitializer)) & TypeFacts.IsUndefined) + !(hasTypeFacts(checkExpression(prop.objectAssignmentInitializer), TypeFacts.IsUndefined)) ) { sourceType = getTypeWithFacts(sourceType, TypeFacts.NEUndefined); } @@ -37638,7 +37656,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkInExpression(left, right, leftType, rightType); case SyntaxKind.AmpersandAmpersandToken: case SyntaxKind.AmpersandAmpersandEqualsToken: { - const resultType = getTypeFacts(leftType) & TypeFacts.Truthy ? + const resultType = hasTypeFacts(leftType, TypeFacts.Truthy) ? getUnionType([extractDefinitelyFalsyTypes(strictNullChecks ? leftType : getBaseTypeOfLiteralType(rightType)), rightType]) : leftType; if (operator === SyntaxKind.AmpersandAmpersandEqualsToken) { @@ -37648,7 +37666,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } case SyntaxKind.BarBarToken: case SyntaxKind.BarBarEqualsToken: { - const resultType = getTypeFacts(leftType) & TypeFacts.Falsy ? + const resultType = hasTypeFacts(leftType, TypeFacts.Falsy) ? getUnionType([getNonNullableType(removeDefinitelyFalsyTypes(leftType)), rightType], UnionReduction.Subtype) : leftType; if (operator === SyntaxKind.BarBarEqualsToken) { @@ -37658,7 +37676,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } case SyntaxKind.QuestionQuestionToken: case SyntaxKind.QuestionQuestionEqualsToken: { - const resultType = getTypeFacts(leftType) & TypeFacts.EQUndefinedOrNull ? + const resultType = hasTypeFacts(leftType, TypeFacts.EQUndefinedOrNull) ? getUnionType([getNonNullableType(leftType), rightType], UnionReduction.Subtype) : leftType; if (operator === SyntaxKind.QuestionQuestionEqualsToken) { @@ -41917,7 +41935,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const type = location === condExpr ? condType : checkTruthinessExpression(location); const isPropertyExpressionCast = isPropertyAccessExpression(location) && isTypeAssertion(location.expression); - if (!(getTypeFacts(type) & TypeFacts.Truthy) || isPropertyExpressionCast) return; + if (!hasTypeFacts(type, TypeFacts.Truthy) || isPropertyExpressionCast) return; // While it technically should be invalid for any known-truthy value // to be tested, we de-scope to functions and Promises unreferenced in From e0a324b0503be479f2b33fd2e17c6e86c94d1297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 24 Aug 2023 19:17:33 +0200 Subject: [PATCH 05/12] Don't parse `DotDotDotToken` in JSX attribute values (#55284) --- src/compiler/parser.ts | 4 ++- ...ImmediateSpreadInAttributeValue.errors.txt | 21 ++++++++++++++ ...ingErrorImmediateSpreadInAttributeValue.js | 20 +++++++++++++ ...rorImmediateSpreadInAttributeValue.symbols | 28 +++++++++++++++++++ ...ErrorImmediateSpreadInAttributeValue.types | 26 +++++++++++++++++ ...ngErrorImmediateSpreadInAttributeValue.tsx | 14 ++++++++++ 6 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.errors.txt create mode 100644 tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.js create mode 100644 tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.symbols create mode 100644 tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.types create mode 100644 tests/cases/conformance/jsx/jsxParsingErrorImmediateSpreadInAttributeValue.tsx diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 08a70b437a973..0e0b9a53b5d9c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6214,7 +6214,9 @@ namespace Parser { let dotDotDotToken: DotDotDotToken | undefined; let expression: Expression | undefined; if (token() !== SyntaxKind.CloseBraceToken) { - dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); + if (!inExpressionContext) { + dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); + } // Only an AssignmentExpression is valid here per the JSX spec, // but we can unambiguously parse a comma sequence and provide // a better error message in grammar checking. diff --git a/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.errors.txt b/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.errors.txt new file mode 100644 index 0000000000000..8e78c781dc76b --- /dev/null +++ b/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.errors.txt @@ -0,0 +1,21 @@ +a.tsx(11,7): error TS1109: Expression expected. +a.tsx(11,11): error TS1003: Identifier expected. + + +==== a.tsx (2 errors) ==== + declare const React: any + declare namespace JSX { + interface IntrinsicElements { + [k: string]: any + } + } + + const X: any + const a: any + + + ~~~ +!!! error TS1109: Expression expected. + ~ +!!! error TS1003: Identifier expected. + \ No newline at end of file diff --git a/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.js b/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.js new file mode 100644 index 0000000000000..8fa436d2428aa --- /dev/null +++ b/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.js @@ -0,0 +1,20 @@ +//// [tests/cases/conformance/jsx/jsxParsingErrorImmediateSpreadInAttributeValue.tsx] //// + +//// [a.tsx] +declare const React: any +declare namespace JSX { + interface IntrinsicElements { + [k: string]: any + } +} + +const X: any +const a: any + + + + +//// [a.js] +var X; +var a; +React.createElement(X, { a: , a: true }); diff --git a/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.symbols b/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.symbols new file mode 100644 index 0000000000000..106dcdfee1f7f --- /dev/null +++ b/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.symbols @@ -0,0 +1,28 @@ +//// [tests/cases/conformance/jsx/jsxParsingErrorImmediateSpreadInAttributeValue.tsx] //// + +=== a.tsx === +declare const React: any +>React : Symbol(React, Decl(a.tsx, 0, 13)) + +declare namespace JSX { +>JSX : Symbol(JSX, Decl(a.tsx, 0, 24)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(a.tsx, 1, 23)) + + [k: string]: any +>k : Symbol(k, Decl(a.tsx, 3, 9)) + } +} + +const X: any +>X : Symbol(X, Decl(a.tsx, 7, 5)) + +const a: any +>a : Symbol(a, Decl(a.tsx, 8, 5)) + + +>X : Symbol(X, Decl(a.tsx, 7, 5)) +>a : Symbol(a, Decl(a.tsx, 10, 2), Decl(a.tsx, 10, 9)) +>a : Symbol(a, Decl(a.tsx, 10, 2), Decl(a.tsx, 10, 9)) + diff --git a/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.types b/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.types new file mode 100644 index 0000000000000..fded984f46ace --- /dev/null +++ b/tests/baselines/reference/jsxParsingErrorImmediateSpreadInAttributeValue.types @@ -0,0 +1,26 @@ +//// [tests/cases/conformance/jsx/jsxParsingErrorImmediateSpreadInAttributeValue.tsx] //// + +=== a.tsx === +declare const React: any +>React : any + +declare namespace JSX { + interface IntrinsicElements { + [k: string]: any +>k : string + } +} + +const X: any +>X : any + +const a: any +>a : any + + +> : any +>X : any +>a : any +> : any +>a : any + diff --git a/tests/cases/conformance/jsx/jsxParsingErrorImmediateSpreadInAttributeValue.tsx b/tests/cases/conformance/jsx/jsxParsingErrorImmediateSpreadInAttributeValue.tsx new file mode 100644 index 0000000000000..cccc5ff03c74a --- /dev/null +++ b/tests/cases/conformance/jsx/jsxParsingErrorImmediateSpreadInAttributeValue.tsx @@ -0,0 +1,14 @@ +// @jsx: react +// @filename: a.tsx + +declare const React: any +declare namespace JSX { + interface IntrinsicElements { + [k: string]: any + } +} + +const X: any +const a: any + + From c3c5abb3a7d960de6e2fb75bc2a74fb90d9109b6 Mon Sep 17 00:00:00 2001 From: TypeScript Bot Date: Fri, 25 Aug 2023 06:18:32 +0000 Subject: [PATCH 06/12] Update package-lock.json --- package-lock.json | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index ae910011b62c0..8ac9d6ef09e27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -937,9 +937,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.4.tgz", - "integrity": "sha512-Y9vbIAoM31djQZrPYjpTLo0XlaSwOIsrlfE3LpulZeRblttsLQRFRlBAppW0LOxyT3ALj2M5vU1ucQQayQH3jA==", + "version": "20.5.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.6.tgz", + "integrity": "sha512-Gi5wRGPbbyOTX+4Y2iULQ27oUPrefaB0PxGQJnfyWN3kvEDGM3mIB5M/gQLmitZf7A9FmLeaqxD3L1CXpm3VKQ==", "dev": true }, "node_modules/@types/semver": { @@ -1394,9 +1394,9 @@ } }, "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", + "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", @@ -3641,9 +3641,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -4407,9 +4407,9 @@ "dev": true }, "@types/node": { - "version": "20.5.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.4.tgz", - "integrity": "sha512-Y9vbIAoM31djQZrPYjpTLo0XlaSwOIsrlfE3LpulZeRblttsLQRFRlBAppW0LOxyT3ALj2M5vU1ucQQayQH3jA==", + "version": "20.5.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.6.tgz", + "integrity": "sha512-Gi5wRGPbbyOTX+4Y2iULQ27oUPrefaB0PxGQJnfyWN3kvEDGM3mIB5M/gQLmitZf7A9FmLeaqxD3L1CXpm3VKQ==", "dev": true }, "@types/semver": { @@ -4715,9 +4715,9 @@ "dev": true }, "chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", + "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -6329,9 +6329,9 @@ } }, "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true }, "typical": { From ec2bd4e252f919290ab7cf852ab16eb73510874c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sampo=20Kivist=C3=B6?= Date: Fri, 25 Aug 2023 21:27:55 +0300 Subject: [PATCH 07/12] perf: replace String and Array indexOf method calls with includes method call (#55482) --- scripts/errorCheck.mjs | 2 +- scripts/eslint/rules/argument-trivia.cjs | 2 +- scripts/update-experimental-branches.mjs | 2 +- src/cancellationToken/cancellationToken.ts | 2 +- src/compiler/checker.ts | 13 ++++----- src/compiler/commandLineParser.ts | 5 ++-- src/compiler/core.ts | 5 ---- src/compiler/emitter.ts | 7 ++--- src/compiler/moduleNameResolver.ts | 29 +++++++++---------- src/compiler/moduleSpecifiers.ts | 9 +++--- src/compiler/parser.ts | 3 +- src/compiler/path.ts | 5 ++-- src/compiler/program.ts | 9 +++--- src/compiler/resolutionCache.ts | 3 +- src/compiler/sys.ts | 5 ++-- src/compiler/transformers/declarations.ts | 3 +- src/compiler/utilities.ts | 13 ++++----- src/executeCommandLine/executeCommandLine.ts | 3 +- src/harness/compilerImpl.ts | 2 +- src/harness/fourslashImpl.ts | 6 ++-- src/harness/harnessIO.ts | 2 +- src/harness/harnessLanguageService.ts | 2 +- src/harness/harnessUtils.ts | 4 +-- src/harness/vpathUtil.ts | 2 +- src/jsTyping/shared.ts | 2 +- src/server/editorServices.ts | 8 ++--- src/server/project.ts | 2 +- src/server/scriptInfo.ts | 5 ++-- src/server/session.ts | 3 +- src/services/codefixes/fixUnusedIdentifier.ts | 2 +- src/services/exportInfoMap.ts | 3 +- src/services/formatting/rules.ts | 2 +- src/services/formatting/smartIndenter.ts | 2 +- src/services/refactors/extractSymbol.ts | 2 +- src/services/services.ts | 3 +- src/services/stringCompletions.ts | 11 ++++--- src/services/utilities.ts | 3 +- src/testRunner/compilerRunner.ts | 2 +- src/testRunner/runner.ts | 2 +- src/testRunner/unittests/helpers/vfs.ts | 2 +- src/testRunner/unittests/moduleResolution.ts | 2 +- .../unittests/tsbuild/graphOrdering.ts | 6 ++-- .../unittests/tsc/declarationEmit.ts | 7 ++--- .../unittests/tscWatch/resolutionCache.ts | 8 ++--- .../unittests/tsserver/typingsInstaller.ts | 4 +-- src/typingsInstaller/nodeTypingsInstaller.ts | 3 +- 46 files changed, 98 insertions(+), 124 deletions(-) diff --git a/scripts/errorCheck.mjs b/scripts/errorCheck.mjs index c2e031e5487da..89c354a56710c 100644 --- a/scripts/errorCheck.mjs +++ b/scripts/errorCheck.mjs @@ -71,7 +71,7 @@ async function checkSourceFiles() { let count = 0; console.log("== List of errors not used in source =="); for (const errName of errorNames) { - if (allSrc.indexOf(errName) < 0) { + if (!allSrc.includes(errName)) { console.log(errName); count++; } diff --git a/scripts/eslint/rules/argument-trivia.cjs b/scripts/eslint/rules/argument-trivia.cjs index 4e3b6011e765b..289a97d79ee4e 100644 --- a/scripts/eslint/rules/argument-trivia.cjs +++ b/scripts/eslint/rules/argument-trivia.cjs @@ -157,7 +157,7 @@ module.exports = createRule({ } } - const hasNewLine = sourceCodeText.slice(commentRangeEnd, argRangeStart).indexOf("\n") >= 0; + const hasNewLine = sourceCodeText.slice(commentRangeEnd, argRangeStart).includes("\n"); if (argRangeStart !== commentRangeEnd + 1 && !hasNewLine) { // TODO(jakebailey): range should be whitespace context.report({ diff --git a/scripts/update-experimental-branches.mjs b/scripts/update-experimental-branches.mjs index 0b1fa533966fc..3f3493296c36f 100644 --- a/scripts/update-experimental-branches.mjs +++ b/scripts/update-experimental-branches.mjs @@ -88,7 +88,7 @@ async function main() { const mergeTree = runSequence([ ["git", ["merge-tree", mergeBase.trim(), branch, "experimental"]], ]); - if (mergeTree.indexOf(`===${"="}===`) >= 0) { // 7 equals is the center of the merge conflict marker + if (mergeTree.includes(`===${"="}===`)) { // 7 equals is the center of the merge conflict marker throw new Error(`Merge conflict detected involving PR ${branch} with other experiment`); } // Merge (always producing a merge commit) diff --git a/src/cancellationToken/cancellationToken.ts b/src/cancellationToken/cancellationToken.ts index 8ea543e01d473..374dddacc80d7 100644 --- a/src/cancellationToken/cancellationToken.ts +++ b/src/cancellationToken/cancellationToken.ts @@ -39,7 +39,7 @@ function createCancellationToken(args: string[]): ServerCancellationToken { // in this case pipe name will be build dynamically as . if (cancellationPipeName.charAt(cancellationPipeName.length - 1) === "*") { const namePrefix = cancellationPipeName.slice(0, -1); - if (namePrefix.length === 0 || namePrefix.indexOf("*") >= 0) { + if (namePrefix.length === 0 || namePrefix.includes("*")) { throw new Error("Invalid name for template cancellation pipe: it should have length greater than 2 characters and contain only one '*'."); } let perRequestPipeName: string | undefined; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 392883ae41fd7..21ed301c23eb6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -964,7 +964,6 @@ import { SpreadElement, startsWith, Statement, - stringContains, StringLiteral, StringLiteralLike, StringLiteralType, @@ -7908,14 +7907,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!specifier) { specifier = getSpecifierForModuleSymbol(chain[0], context); } - if (!(context.flags & NodeBuilderFlags.AllowNodeModulesRelativePaths) && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Classic && specifier.indexOf("/node_modules/") >= 0) { + if (!(context.flags & NodeBuilderFlags.AllowNodeModulesRelativePaths) && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Classic && specifier.includes("/node_modules/")) { const oldSpecifier = specifier; if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node16 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) { // We might be able to write a portable import type using a mode override; try specifier generation again, but with a different mode set const swappedMode = contextFile?.impliedNodeFormat === ModuleKind.ESNext ? ModuleKind.CommonJS : ModuleKind.ESNext; specifier = getSpecifierForModuleSymbol(chain[0], context, swappedMode); - if (specifier.indexOf("/node_modules/") >= 0) { + if (specifier.includes("/node_modules/")) { // Still unreachable :( specifier = oldSpecifier; } @@ -8661,7 +8660,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (group.length > 1) { // remove group members from statements and then merge group members and add back to statements statements = [ - ...filter(statements, s => group.indexOf(s as ExportDeclaration) === -1), + ...filter(statements, s => !group.includes(s as ExportDeclaration)), factory.createExportDeclaration( /*modifiers*/ undefined, /*isTypeOnly*/ false, @@ -24250,7 +24249,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const originalKeywordKind = identifierToKeywordKind(param.name); if ( (isCallSignatureDeclaration(param.parent) || isMethodSignature(param.parent) || isFunctionTypeNode(param.parent)) && - param.parent.parameters.indexOf(param) > -1 && + param.parent.parameters.includes(param) && (resolveName(param, param.name.escapedText, SymbolFlags.Type, /*nameNotFoundMessage*/ undefined, param.name.escapedText, /*isUse*/ true) || originalKeywordKind && isTypeNodeKind(originalKeywordKind)) ) { @@ -30962,7 +30961,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isHyphenatedJsxName(name: string | __String) { - return stringContains(name as string, "-"); + return (name as string).includes("-"); } /** @@ -49648,7 +49647,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Realism (size) checking // We should test against `getTextOfNode(node)` rather than `node.text`, because `node.text` for large numeric literals can contain "." // e.g. `node.text` for numeric literal `1100000000000000000000` is `1.1e21`. - const isFractional = getTextOfNode(node).indexOf(".") !== -1; + const isFractional = getTextOfNode(node).includes("."); const isScientific = node.numericLiteralFlags & TokenFlags.Scientific; // Scientific notation (e.g. 2e54 and 1e00000000010) can't be converted to bigint diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 2bde2526ab0c3..61058f8e8be26 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -104,7 +104,6 @@ import { returnTrue, ScriptTarget, startsWith, - stringContains, StringLiteral, SyntaxKind, sys, @@ -1710,7 +1709,7 @@ export function parseListTypeOption(opt: CommandLineOptionOfListType, value = "" if (startsWith(value, "-")) { return undefined; } - if (opt.type === "listOrElement" && !stringContains(value, ",")) { + if (opt.type === "listOrElement" && !value.includes(",")) { return validateJsonOptionValue(opt, value, errors); } if (value === "") { @@ -3078,7 +3077,7 @@ function parseConfig( basePath = normalizeSlashes(basePath); const resolvedPath = getNormalizedAbsolutePath(configFileName || "", basePath); - if (resolutionStack.indexOf(resolvedPath) >= 0) { + if (resolutionStack.includes(resolvedPath)) { errors.push(createCompilerDiagnostic(Diagnostics.Circularity_detected_while_resolving_configuration_Colon_0, [...resolutionStack, resolvedPath].join(" -> "))); return { raw: json || convertToObject(sourceFile!, errors) }; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 340b49b134aa3..fbd2432e9b79b 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2484,11 +2484,6 @@ export function tryRemoveSuffix(str: string, suffix: string): string | undefined return endsWith(str, suffix) ? str.slice(0, str.length - suffix.length) : undefined; } -/** @internal */ -export function stringContains(str: string, substring: string): boolean { - return str.indexOf(substring) !== -1; -} - /** * Takes a string like "jquery-min.4.2.3" and returns "jquery" * diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d407dadc246ae..4a0b03f7285d5 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -407,7 +407,6 @@ import { SpreadElement, stableSort, Statement, - stringContains, StringLiteral, supportedJSExtensionsFlat, SwitchStatement, @@ -3065,9 +3064,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // If the number will be printed verbatim and it doesn't already contain a dot or an exponent indicator, add one // if the expression doesn't have any comments that will be emitted. return !(expression.numericLiteralFlags & TokenFlags.WithSpecifier) - && !stringContains(text, tokenToString(SyntaxKind.DotToken)!) - && !stringContains(text, String.fromCharCode(CharacterCodes.E)) - && !stringContains(text, String.fromCharCode(CharacterCodes.e)); + && !text.includes(tokenToString(SyntaxKind.DotToken)!) + && !text.includes(String.fromCharCode(CharacterCodes.E)) + && !text.includes(String.fromCharCode(CharacterCodes.e)); } else if (isAccessExpression(expression)) { // check if constant enum value is a non-negative integer diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 4aedc4a4aa5d0..0a5088a4c34b9 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -100,7 +100,6 @@ import { sort, SourceFile, startsWith, - stringContains, supportedDeclarationExtensions, supportedJSExtensionsFlat, supportedTSImplementationExtensions, @@ -1808,7 +1807,7 @@ function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleNa && features & NodeResolutionFeatures.Exports && !isExternalModuleNameRelative(moduleName) && !extensionIsOk(Extensions.TypeScript | Extensions.Declaration, result.value.resolved.extension) - && conditions.indexOf("import") > -1 + && conditions.includes("import") ) { traceIfEnabled(state, Diagnostics.Resolution_of_non_relative_name_failed_trying_with_modern_Node_resolution_features_disabled_to_see_if_npm_library_needs_configuration_update); const diagnosticState = { @@ -1849,7 +1848,7 @@ function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleNa resolved = loadModuleFromSelfNameReference(extensions, moduleName, containingDirectory, state, cache, redirectedReference); } if (!resolved) { - if (moduleName.indexOf(":") > -1) { + if (moduleName.includes(":")) { if (traceEnabled) { trace(host, Diagnostics.Skipping_module_0_that_looks_like_an_absolute_URI_target_file_types_Colon_1, moduleName, formatExtensions(extensions)); } @@ -1944,7 +1943,7 @@ function nodeLoadModuleByRelativeName(extensions: Extensions, candidate: string, export const nodeModulesPathPart = "/node_modules/"; /** @internal */ export function pathContainsNodeModules(path: string): boolean { - return stringContains(path, nodeModulesPathPart); + return path.includes(nodeModulesPathPart); } /** @@ -2006,7 +2005,7 @@ function loadModuleFromFile(extensions: Extensions, candidate: string, onlyRecor function loadModuleFromFileNoImplicitExtensions(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined { const filename = getBaseFileName(candidate); - if (filename.indexOf(".") === -1) { + if (!filename.includes(".")) { return undefined; // extensionless import, no lookups performed, since we don't support extensionless files } let extensionless = removeFileExtension(candidate); @@ -2223,7 +2222,7 @@ function loadEntrypointsFromExportMap( function loadEntrypointsFromTargetExports(target: unknown): boolean | undefined { if (typeof target === "string" && startsWith(target, "./")) { - if (target.indexOf("*") >= 0 && state.host.readDirectory) { + if (target.includes("*") && state.host.readDirectory) { if (target.indexOf("*") !== target.lastIndexOf("*")) { return false; } @@ -2243,7 +2242,7 @@ function loadEntrypointsFromExportMap( } else { const partsAfterFirst = getPathComponents(target).slice(2); - if (partsAfterFirst.indexOf("..") >= 0 || partsAfterFirst.indexOf(".") >= 0 || partsAfterFirst.indexOf("node_modules") >= 0) { + if (partsAfterFirst.includes("..") || partsAfterFirst.includes(".") || partsAfterFirst.includes("node_modules")) { return false; } const resolvedTarget = combinePaths(scope.packageDirectory, target); @@ -2609,11 +2608,11 @@ export function comparePatternKeys(a: string, b: string) { function loadModuleFromImportsOrExports(extensions: Extensions, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined, moduleName: string, lookupTable: object, scope: PackageJsonInfo, isImports: boolean): SearchResult | undefined { const loadModuleFromTargetImportOrExport = getLoadModuleFromTargetImportOrExport(extensions, state, cache, redirectedReference, moduleName, scope, isImports); - if (!endsWith(moduleName, directorySeparator) && moduleName.indexOf("*") === -1 && hasProperty(lookupTable, moduleName)) { + if (!endsWith(moduleName, directorySeparator) && !moduleName.includes("*") && hasProperty(lookupTable, moduleName)) { const target = (lookupTable as { [idx: string]: unknown; })[moduleName]; return loadModuleFromTargetImportOrExport(target, /*subpath*/ "", /*pattern*/ false, moduleName); } - const expandingKeys = sort(filter(getOwnKeys(lookupTable as MapLike), k => k.indexOf("*") !== -1 || endsWith(k, "/")), comparePatternKeys); + const expandingKeys = sort(filter(getOwnKeys(lookupTable as MapLike), k => k.includes("*") || endsWith(k, "/")), comparePatternKeys); for (const potentialTarget of expandingKeys) { if (state.features & NodeResolutionFeatures.ExportsPatternTrailers && matchesPatternWithTrailer(potentialTarget, moduleName)) { const target = (lookupTable as { [idx: string]: unknown; })[potentialTarget]; @@ -2677,7 +2676,7 @@ function getLoadModuleFromTargetImportOrExport(extensions: Extensions, state: Mo } const parts = pathIsRelative(target) ? getPathComponents(target).slice(1) : getPathComponents(target); const partsAfterFirst = parts.slice(1); - if (partsAfterFirst.indexOf("..") >= 0 || partsAfterFirst.indexOf(".") >= 0 || partsAfterFirst.indexOf("node_modules") >= 0) { + if (partsAfterFirst.includes("..") || partsAfterFirst.includes(".") || partsAfterFirst.includes("node_modules")) { if (state.traceEnabled) { trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName); } @@ -2687,7 +2686,7 @@ function getLoadModuleFromTargetImportOrExport(extensions: Extensions, state: Mo // TODO: Assert that `resolvedTarget` is actually within the package directory? That's what the spec says.... but I'm not sure we need // to be in the business of validating everyone's import and export map correctness. const subpathParts = getPathComponents(subpath); - if (subpathParts.indexOf("..") >= 0 || subpathParts.indexOf(".") >= 0 || subpathParts.indexOf("node_modules") >= 0) { + if (subpathParts.includes("..") || subpathParts.includes(".") || subpathParts.includes("node_modules")) { if (state.traceEnabled) { trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName); } @@ -2706,7 +2705,7 @@ function getLoadModuleFromTargetImportOrExport(extensions: Extensions, state: Mo if (!Array.isArray(target)) { traceIfEnabled(state, Diagnostics.Entering_conditional_exports); for (const condition of getOwnKeys(target as MapLike)) { - if (condition === "default" || state.conditions.indexOf(condition) >= 0 || isApplicableVersionedTypesKey(state.conditions, condition)) { + if (condition === "default" || state.conditions.includes(condition) || isApplicableVersionedTypesKey(state.conditions, condition)) { traceIfEnabled(state, Diagnostics.Matched_0_condition_1, isImports ? "imports" : "exports", condition); const subTarget = (target as MapLike)[condition]; const result = loadModuleFromTargetImportOrExport(subTarget, subpath, pattern, key); @@ -2772,7 +2771,7 @@ function getLoadModuleFromTargetImportOrExport(extensions: Extensions, state: Mo if ( !state.isConfigLookup && (state.compilerOptions.declarationDir || state.compilerOptions.outDir) - && finalPath.indexOf("/node_modules/") === -1 + && !finalPath.includes("/node_modules/") && (state.compilerOptions.configFile ? containsPath(scope.packageDirectory, toAbsolutePath(state.compilerOptions.configFile.fileName), !useCaseSensitiveFileNames(state)) : true) ) { // So that all means we'll only try these guesses for files outside `node_modules` in a directory where the `package.json` and `tsconfig.json` are siblings. @@ -2876,7 +2875,7 @@ function getLoadModuleFromTargetImportOrExport(extensions: Extensions, state: Mo /** @internal */ export function isApplicableVersionedTypesKey(conditions: readonly string[], key: string) { - if (conditions.indexOf("types") === -1) return false; // only apply versioned types conditions if the types condition is applied + if (!conditions.includes("types")) return false; // only apply versioned types conditions if the types condition is applied if (!startsWith(key, "types@")) return false; const range = VersionRange.tryParse(key.substring("types@".length)); if (!range) return false; @@ -3099,7 +3098,7 @@ export function getPackageNameFromTypesPackageName(mangledName: string): string /** @internal */ export function unmangleScopedPackageName(typesPackageName: string): string { - return stringContains(typesPackageName, mangledScopedPackageSeparator) ? + return typesPackageName.includes(mangledScopedPackageSeparator) ? "@" + typesPackageName.replace(mangledScopedPackageSeparator, directorySeparator) : typesPackageName; } diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index cec0585ca3650..2a59e6c5be814 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -101,7 +101,6 @@ import { SourceFile, startsWith, startsWithDirectory, - stringContains, StringLiteral, Symbol, SymbolFlags, @@ -866,7 +865,7 @@ function tryGetModuleNameFromExports(options: CompilerOptions, targetFilePath: s return forEach(getOwnKeys(exports as MapLike), k => { const subPackageName = getNormalizedAbsolutePath(combinePaths(packageName, k), /*currentDirectory*/ undefined); const mode = endsWith(k, "/") ? MatchingMode.Directory - : stringContains(k, "*") ? MatchingMode.Pattern + : k.includes("*") ? MatchingMode.Pattern : MatchingMode.Exact; return tryGetModuleNameFromExports(options, targetFilePath, packageDirectory, subPackageName, (exports as MapLike)[k], conditions, mode); }); @@ -874,7 +873,7 @@ function tryGetModuleNameFromExports(options: CompilerOptions, targetFilePath: s else { // conditional mapping for (const key of getOwnKeys(exports as MapLike)) { - if (key === "default" || conditions.indexOf(key) >= 0 || isApplicableVersionedTypesKey(conditions, key)) { + if (key === "default" || conditions.includes(key) || isApplicableVersionedTypesKey(conditions, key)) { const subTarget = (exports as MapLike)[key]; const result = tryGetModuleNameFromExports(options, targetFilePath, packageDirectory, packageName, subTarget, conditions, mode); if (result) { @@ -1093,7 +1092,7 @@ function processEnding(fileName: string, allowedEndings: readonly ModuleSpecifie else if (fileExtensionIsOneOf(fileName, [Extension.Dmts, Extension.Mts, Extension.Dcts, Extension.Cts])) { return noExtension + getJSExtensionForFile(fileName, options); } - else if (!fileExtensionIsOneOf(fileName, [Extension.Dts]) && fileExtensionIsOneOf(fileName, [Extension.Ts]) && stringContains(fileName, ".d.")) { + else if (!fileExtensionIsOneOf(fileName, [Extension.Dts]) && fileExtensionIsOneOf(fileName, [Extension.Ts]) && fileName.includes(".d.")) { // `foo.d.json.ts` and the like - remap back to `foo.json` return tryGetRealFileNameForNonJsDeclarationFileName(fileName)!; } @@ -1129,7 +1128,7 @@ function processEnding(fileName: string, allowedEndings: readonly ModuleSpecifie /** @internal */ export function tryGetRealFileNameForNonJsDeclarationFileName(fileName: string) { const baseName = getBaseFileName(fileName); - if (!endsWith(fileName, Extension.Ts) || !stringContains(baseName, ".d.") || fileExtensionIsOneOf(baseName, [Extension.Dts])) return undefined; + if (!endsWith(fileName, Extension.Ts) || !baseName.includes(".d.") || fileExtensionIsOneOf(baseName, [Extension.Dts])) return undefined; const noExtension = removeExtension(fileName, Extension.Ts); const ext = noExtension.substring(noExtension.lastIndexOf(".")); return noExtension.substring(0, noExtension.indexOf(".d.")) + ext; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 0e0b9a53b5d9c..ad0f132b4e5ab 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -338,7 +338,6 @@ import { SpreadElement, startsWith, Statement, - stringContains, StringLiteral, supportedDeclarationExtensions, SwitchStatement, @@ -10408,7 +10407,7 @@ namespace IncrementalParser { /** @internal */ export function isDeclarationFileName(fileName: string): boolean { - return fileExtensionIsOneOf(fileName, supportedDeclarationExtensions) || (fileExtensionIs(fileName, Extension.Ts) && stringContains(getBaseFileName(fileName), ".d.")); + return fileExtensionIsOneOf(fileName, supportedDeclarationExtensions) || (fileExtensionIs(fileName, Extension.Ts) && getBaseFileName(fileName).includes(".d.")); } function parseResolutionMode(mode: string | undefined, pos: number, end: number, reportDiagnostic: PragmaDiagnosticReporter): ResolutionMode { diff --git a/src/compiler/path.ts b/src/compiler/path.ts index c77fa141f071d..685f28158901a 100644 --- a/src/compiler/path.ts +++ b/src/compiler/path.ts @@ -15,7 +15,6 @@ import { Path, some, startsWith, - stringContains, } from "./_namespaces/ts"; /** @@ -113,7 +112,7 @@ export function pathIsBareSpecifier(path: string): boolean { /** @internal */ export function hasExtension(fileName: string): boolean { - return stringContains(getBaseFileName(fileName), "."); + return getBaseFileName(fileName).includes("."); } /** @internal */ @@ -524,7 +523,7 @@ export function getPathFromPathComponents(pathComponents: read * @internal */ export function normalizeSlashes(path: string): string { - return path.indexOf("\\") !== -1 + return path.includes("\\") ? path.replace(backslashRegExp, directorySeparator) : path; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 9e8af35bf4e62..294a0e7200702 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -304,7 +304,6 @@ import { stableSort, startsWith, Statement, - stringContains, StringLiteral, StringLiteralLike, StructureIsReused, @@ -2016,7 +2015,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // but the resolved real path may be the .d.ts from project reference // Note:: Currently we try the real path only if the // file is from node_modules to avoid having to run real path on all file paths - if (!host.realpath || !options.preserveSymlinks || !stringContains(file.originalFileName, nodeModulesPathPart)) return undefined; + if (!host.realpath || !options.preserveSymlinks || !file.originalFileName.includes(nodeModulesPathPart)) return undefined; const realDeclarationPath = toPath(host.realpath(file.originalFileName)); return realDeclarationPath === file.path ? undefined : getRedirectReferenceForResolutionFromSourceOfProject(realDeclarationPath); } @@ -3565,7 +3564,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg host.realpath && options.preserveSymlinks && isDeclarationFileName(fileName) && - stringContains(fileName, nodeModulesPathPart) + fileName.includes(nodeModulesPathPart) ) { const realPath = toPath(host.realpath(fileName)); if (realPath !== path) source = getSourceOfProjectReferenceRedirect(realPath); @@ -5090,7 +5089,7 @@ function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSource if (!host.getResolvedProjectReferences() || containsIgnoredPath(directory)) return; // Because we already watch node_modules, handle symlinks in there - if (!originalRealpath || !stringContains(directory, nodeModulesPathPart)) return; + if (!originalRealpath || !directory.includes(nodeModulesPathPart)) return; const symlinkCache = host.getSymlinkCache(); const directoryPath = ensureTrailingDirectorySeparator(host.toPath(directory)); if (symlinkCache.getSymlinkedDirectories()?.has(directoryPath)) return; @@ -5124,7 +5123,7 @@ function updateHostForUseSourceOfProjectReferenceRedirect(host: HostForUseSource const symlinkedDirectories = symlinkCache.getSymlinkedDirectories(); if (!symlinkedDirectories) return false; const fileOrDirectoryPath = host.toPath(fileOrDirectory); - if (!stringContains(fileOrDirectoryPath, nodeModulesPathPart)) return false; + if (!fileOrDirectoryPath.includes(nodeModulesPathPart)) return false; if (isFile && symlinkCache.getSymlinkedFiles()?.has(fileOrDirectoryPath)) return true; // If it contains node_modules check if its one of the symlinked path we know of diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index fccc476dbfb60..0c12c996c8416 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -72,7 +72,6 @@ import { some, SourceFile, startsWith, - stringContains, StringLiteralLike, trace, updateResolutionField, @@ -219,7 +218,7 @@ export function removeIgnoredPath(path: Path): Path | undefined { return removeSuffix(path, "/.staging") as Path; } - return some(ignoredPaths, searchPath => stringContains(path, searchPath)) ? + return some(ignoredPaths, searchPath => path.includes(searchPath)) ? undefined : path; } diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 14e6f04b77afa..d888a3d599bb3 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -39,7 +39,6 @@ import { resolveJSModule, some, startsWith, - stringContains, timestamp, unorderedRemoveItem, WatchDirectoryKind, @@ -814,9 +813,9 @@ function createDirectoryWatcherSupportingRecursive({ } function isInPath(path: string, searchPath: string) { - if (stringContains(path, searchPath)) return true; + if (path.includes(searchPath)) return true; if (useCaseSensitiveFileNames) return false; - return stringContains(toCanonicalFilePath(path), searchPath); + return toCanonicalFilePath(path).includes(searchPath); } } diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 8fb87a7b281d4..048615abb1f13 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -202,7 +202,6 @@ import { SourceFile, startsWith, Statement, - stringContains, StringLiteral, Symbol, SymbolAccessibility, @@ -241,7 +240,7 @@ export function getDeclarationDiagnostics(host: EmitHost, resolver: EmitResolver function hasInternalAnnotation(range: CommentRange, currentSourceFile: SourceFile) { const comment = currentSourceFile.text.substring(range.pos, range.end); - return stringContains(comment, "@internal"); + return comment.includes("@internal"); } /** @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 02b2278e93b5d..bd5958feaf660 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -492,7 +492,6 @@ import { startsWith, startsWithUseStrict, Statement, - stringContains, StringLiteral, StringLiteralLike, StringLiteralType, @@ -802,7 +801,7 @@ export function createModuleNotFoundChain(sourceFile: SourceFile, host: TypeChec /*details*/ undefined, Diagnostics.There_are_types_at_0_but_this_result_could_not_be_resolved_when_respecting_package_json_exports_The_1_library_may_need_to_update_its_package_json_or_typings, node10Result, - node10Result.indexOf(nodeModulesPathPart + "@types/") > -1 ? `@types/${mangleScopedPackageName(packageName)}` : packageName, + node10Result.includes(nodeModulesPathPart + "@types/") ? `@types/${mangleScopedPackageName(packageName)}` : packageName, ) : host.typesPackageExists(packageName) ? chainDiagnosticMessages( @@ -5986,7 +5985,7 @@ function isQuoteOrBacktick(charCode: number) { /** @internal */ export function isIntrinsicJsxName(name: __String | string) { const ch = (name as string).charCodeAt(0); - return (ch >= CharacterCodes.a && ch <= CharacterCodes.z) || stringContains(name as string, "-"); + return (ch >= CharacterCodes.a && ch <= CharacterCodes.z) || (name as string).includes("-"); } const indentStrings: string[] = ["", " "]; @@ -6007,7 +6006,7 @@ export function getIndentSize() { /** @internal */ export function isNightly() { - return stringContains(version, "-dev") || stringContains(version, "-insiders"); + return version.includes("-dev") || version.includes("-insiders"); } /** @internal */ @@ -6236,7 +6235,7 @@ export function getExternalModuleNameFromDeclaration(host: ResolveModuleNameReso const specifier = getExternalModuleName(declaration); if ( specifier && isStringLiteralLike(specifier) && !pathIsRelative(specifier.text) && - getCanonicalAbsolutePath(host, file.path).indexOf(getCanonicalAbsolutePath(host, ensureTrailingDirectorySeparator(host.getCommonSourceDirectory()))) === -1 + !getCanonicalAbsolutePath(host, file.path).includes(getCanonicalAbsolutePath(host, ensureTrailingDirectorySeparator(host.getCommonSourceDirectory()))) ) { return undefined; } @@ -9335,7 +9334,7 @@ export function getSupportedExtensions(options?: CompilerOptions, extraFileExten const flatBuiltins = flatten(builtins); const extensions = [ ...builtins, - ...mapDefined(extraFileExtensions, x => x.scriptKind === ScriptKind.Deferred || needJsExtensions && isJSLike(x.scriptKind) && flatBuiltins.indexOf(x.extension as Extension) === -1 ? [x.extension] : undefined), + ...mapDefined(extraFileExtensions, x => x.scriptKind === ScriptKind.Deferred || needJsExtensions && isJSLike(x.scriptKind) && !flatBuiltins.includes(x.extension as Extension) ? [x.extension] : undefined), ]; return extensions; @@ -10052,7 +10051,7 @@ export function expressionResultIsUnused(node: Expression): boolean { /** @internal */ export function containsIgnoredPath(path: string) { - return some(ignoredPaths, p => stringContains(path, p)); + return some(ignoredPaths, p => path.includes(p)); } /** @internal */ diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index 075cbae2c3be8..8417f15944241 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -77,7 +77,6 @@ import { SourceFile, startsWith, startTracing, - stringContains, supportedJSExtensionsFlat, supportedTSExtensionsFlat, sys, @@ -193,7 +192,7 @@ function createColors(sys: System) { return `\x1b[1m${str}\x1b[22m`; } - const isWindows = sys.getEnvironmentVariable("OS") && stringContains(sys.getEnvironmentVariable("OS").toLowerCase(), "windows"); + const isWindows = sys.getEnvironmentVariable("OS") && sys.getEnvironmentVariable("OS").toLowerCase().includes("windows"); const isWindowsTerminal = sys.getEnvironmentVariable("WT_SESSION"); const isVSCode = sys.getEnvironmentVariable("TERM_PROGRAM") && sys.getEnvironmentVariable("TERM_PROGRAM") === "vscode"; diff --git a/src/harness/compilerImpl.ts b/src/harness/compilerImpl.ts index 1a37603da1e41..370f09bcf69e2 100644 --- a/src/harness/compilerImpl.ts +++ b/src/harness/compilerImpl.ts @@ -213,7 +213,7 @@ export class CompilationResult { } else { path = vpath.resolve(this.vfs.cwd(), path); - const outDir = ext === ".d.ts" || ext === ".d.mts" || ext === ".d.cts" || (ext.endsWith(".ts") || ts.stringContains(ext, ".d.")) ? this.options.declarationDir || this.options.outDir : this.options.outDir; + const outDir = ext === ".d.ts" || ext === ".d.mts" || ext === ".d.cts" || (ext.endsWith(".ts") || ext.includes(".d.")) ? this.options.declarationDir || this.options.outDir : this.options.outDir; if (outDir) { const common = this.commonSourceDirectory; if (common) { diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 4ff4e17e66406..8f9a5e5738f36 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -440,7 +440,7 @@ export class TestState { const keys = ts.getAllKeys(ls); for (const k of keys) { const key = k as keyof typeof ls; - if (cacheableMembers.indexOf(key) === -1) { + if (!cacheableMembers.includes(key)) { proxy[key] = (...args: any[]) => (ls[key] as (...args: any[]) => any)(...args); continue; } @@ -4321,7 +4321,7 @@ export class TestState { private tryFindFileWorker(name: string): { readonly file: FourSlashFile | undefined; readonly availableNames: readonly string[]; } { name = ts.normalizePath(name); // names are stored in the compiler with this relative path, this allows people to use goTo.file on just the fileName - name = name.indexOf("/") === -1 ? (this.basePath + "/" + name) : name; + name = name.includes("/") ? name : (this.basePath + "/" + name); const availableNames: string[] = []; const file = ts.forEach(this.testData.files, file => { @@ -4905,7 +4905,7 @@ function parseFileContent(content: string, fileName: string, markerMap: Map -1 && referencedExtensions.indexOf(".d.ts") === -1) { + if (extension === ".ts" || referencedExtensions && referencedExtensions.includes(".ts") && !referencedExtensions.includes(".d.ts")) { // special-case and filter .d.ts out of .ts results existing = existing.filter(f => !ts.endsWith(f, ".d.ts")); } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 96db9786a565b..1fed0e38ef4ca 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -846,7 +846,7 @@ class SessionServerHost implements ts.server.ServerHost, ts.server.Logger { } readFile(fileName: string): string | undefined { - if (ts.stringContains(fileName, Compiler.defaultLibFileName)) { + if (fileName.includes(Compiler.defaultLibFileName)) { fileName = Compiler.defaultLibFileName; } diff --git a/src/harness/harnessUtils.ts b/src/harness/harnessUtils.ts index 95c6068ab0702..752e52bdba925 100644 --- a/src/harness/harnessUtils.ts +++ b/src/harness/harnessUtils.ts @@ -33,7 +33,7 @@ export function splitContentByNewlines(content: string) { /** Reads a file under /tests */ export function readTestFile(path: string) { - if (path.indexOf("tests") < 0) { + if (!path.includes("tests")) { path = "tests/" + path; } @@ -138,7 +138,7 @@ export function assertInvariants(node: ts.Node | undefined, parent: ts.Node | un } const child = (node as any)[childName]; if (isNodeOrArray(child)) { - assert.isFalse(childNodesAndArrays.indexOf(child) < 0, "Missing child when forEach'ing over node: " + ts.Debug.formatSyntaxKind(node.kind) + "-" + childName); + assert.isFalse(!childNodesAndArrays.includes(child), "Missing child when forEach'ing over node: " + ts.Debug.formatSyntaxKind(node.kind) + "-" + childName); } } } diff --git a/src/harness/vpathUtil.ts b/src/harness/vpathUtil.ts index 0e84144cf0958..9b9d5e79fdfe6 100644 --- a/src/harness/vpathUtil.ts +++ b/src/harness/vpathUtil.ts @@ -133,5 +133,5 @@ export function isDefaultLibrary(path: string) { } export function isTsConfigFile(path: string): boolean { - return path.indexOf("tsconfig") !== -1 && path.indexOf("json") !== -1; + return path.includes("tsconfig") && path.includes("json"); } diff --git a/src/jsTyping/shared.ts b/src/jsTyping/shared.ts index 5ae0e09354c75..3330d12afb15f 100644 --- a/src/jsTyping/shared.ts +++ b/src/jsTyping/shared.ts @@ -49,7 +49,7 @@ export namespace Arguments { /** @internal */ export function hasArgument(argumentName: string) { - return sys.args.indexOf(argumentName) >= 0; + return sys.args.includes(argumentName); } /** @internal */ diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 158e08f25d30e..ab02f3548d4bd 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -4093,7 +4093,7 @@ export class ProjectService { for (const type of rule.types) { // Best-effort de-duping here - doesn't need to be unduplicated but // we don't want the list to become a 400-element array of just 'kendo' - if (typeAcqInclude.indexOf(type) < 0) { + if (!typeAcqInclude.includes(type)) { typeAcqInclude.push(type); } } @@ -4118,7 +4118,7 @@ export class ProjectService { }).join(""); }); - if (excludeRules.indexOf(processedRule) === -1) { + if (!excludeRules.includes(processedRule)) { excludeRules.push(processedRule); } } @@ -4126,7 +4126,7 @@ export class ProjectService { else { // If not rules listed, add the default rule to exclude the matched file const escaped = ProjectService.escapeFilenameForRegex(root); - if (excludeRules.indexOf(escaped) < 0) { + if (!excludeRules.includes(escaped)) { excludeRules.push(escaped); } } @@ -4155,7 +4155,7 @@ export class ProjectService { exclude = true; // ... but *include* it in the list of types to acquire // Same best-effort dedupe as above - if (typeAcqInclude.indexOf(typeName) < 0) { + if (!typeAcqInclude.includes(typeName)) { typeAcqInclude.push(typeName); } } diff --git a/src/server/project.ts b/src/server/project.ts index 97451f224c7f7..8df08a36cd747 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1503,7 +1503,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo protected removeExistingTypings(include: string[]): string[] { const existing = getAutomaticTypeDirectiveNames(this.getCompilerOptions(), this.directoryStructureHost); - return include.filter(i => existing.indexOf(i) < 0); + return include.filter(i => !existing.includes(i)); } private updateGraphWorker() { diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 57a9d06c911fb..ca9f11d2e612a 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -31,7 +31,6 @@ import { some, SourceFile, SourceFileLike, - stringContains, TextSpan, unorderedRemoveItem, } from "./_namespaces/ts"; @@ -339,9 +338,9 @@ export class TextStorage { export function isDynamicFileName(fileName: NormalizedPath) { return fileName[0] === "^" || - ((stringContains(fileName, "walkThroughSnippet:/") || stringContains(fileName, "untitled:/")) && + ((fileName.includes("walkThroughSnippet:/") || fileName.includes("untitled:/")) && getBaseFileName(fileName)[0] === "^") || - (stringContains(fileName, ":^") && !stringContains(fileName, directorySeparator)); + (fileName.includes(":^") && !fileName.includes(directorySeparator)); } /** @internal */ diff --git a/src/server/session.ts b/src/server/session.ts index 5a95d12e26dec..39a57df5ebfcf 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -124,7 +124,6 @@ import { some, SourceFile, startsWith, - stringContains, SymbolDisplayPart, SyntaxKind, TextChange, @@ -2960,7 +2959,7 @@ export class Session implements EventSender { } // No need to analyze lib.d.ts - const fileNamesInProject = fileNames!.filter(value => !stringContains(value, "lib.d.ts")); // TODO: GH#18217 + const fileNamesInProject = fileNames!.filter(value => !value.includes("lib.d.ts")); // TODO: GH#18217 if (fileNamesInProject.length === 0) { return; } diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index 494721afb5ce5..44f7dec260742 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -415,7 +415,7 @@ function mayDeleteParameter(checker: TypeChecker, sourceFile: SourceFile, parame } function isCallbackLike(checker: TypeChecker, sourceFile: SourceFile, name: Identifier): boolean { - return !!FindAllReferences.Core.eachSymbolReferenceInFile(name, checker, sourceFile, reference => isIdentifier(reference) && isCallExpression(reference.parent) && reference.parent.arguments.indexOf(reference) >= 0); + return !!FindAllReferences.Core.eachSymbolReferenceInFile(name, checker, sourceFile, reference => isIdentifier(reference) && isCallExpression(reference.parent) && reference.parent.arguments.includes(reference)); } function isLastParameter(func: FunctionLikeDeclaration, parameter: ParameterDeclaration, isFixAll: boolean): boolean { diff --git a/src/services/exportInfoMap.ts b/src/services/exportInfoMap.ts index 9a4d9cf1d2b7c..1b8cfe67f55c4 100644 --- a/src/services/exportInfoMap.ts +++ b/src/services/exportInfoMap.ts @@ -49,7 +49,6 @@ import { SourceFile, startsWith, Statement, - stringContains, stripQuotes, Symbol, SymbolFlags, @@ -443,7 +442,7 @@ export function forEachExternalModuleToImportFrom( function forEachExternalModule(checker: TypeChecker, allSourceFiles: readonly SourceFile[], excludePatterns: readonly RegExp[] | undefined, cb: (module: Symbol, sourceFile: SourceFile | undefined) => void) { const isExcluded = excludePatterns && ((fileName: string) => excludePatterns.some(p => p.test(fileName))); for (const ambient of checker.getAmbientModules()) { - if (!stringContains(ambient.name, "*") && !(excludePatterns && ambient.declarations?.every(d => isExcluded!(d.getSourceFile().fileName)))) { + if (!ambient.name.includes("*") && !(excludePatterns && ambient.declarations?.every(d => isExcluded!(d.getSourceFile().fileName)))) { cb(ambient, /*sourceFile*/ undefined); } } diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index f57de9336d92e..98b8be1fe8c21 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -971,5 +971,5 @@ function isSemicolonInsertionContext(context: FormattingContext): boolean { function isNotPropertyAccessOnIntegerLiteral(context: FormattingContext): boolean { return !isPropertyAccessExpression(context.contextNode) || !isNumericLiteral(context.contextNode.expression) - || context.contextNode.expression.getText().indexOf(".") !== -1; + || context.contextNode.expression.getText().includes("."); } diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index d2468b1b2e10b..da411914dff5c 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -150,7 +150,7 @@ export namespace SmartIndenter { const containerList = getListByPosition(position, precedingToken.parent, sourceFile); // use list position if the preceding token is before any list items if (containerList && !rangeContainsRange(containerList, precedingToken)) { - const useTheSameBaseIndentation = [SyntaxKind.FunctionExpression, SyntaxKind.ArrowFunction].indexOf(currentToken.parent.kind) !== -1; + const useTheSameBaseIndentation = [SyntaxKind.FunctionExpression, SyntaxKind.ArrowFunction].includes(currentToken.parent.kind); const indentSize = useTheSameBaseIndentation ? 0 : options.indentSize!; return getActualIndentationForListStartLine(containerList, sourceFile, options) + indentSize; // TODO: GH#18217 } diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 906ddefbcd1c7..f25098e2c060a 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -2128,7 +2128,7 @@ function collectReadsAndWrites( function checkForUsedDeclarations(node: Node) { // If this node is entirely within the original extraction range, we don't need to do anything. - if (node === targetRange.range || (isReadonlyArray(targetRange.range) && targetRange.range.indexOf(node as Statement) >= 0)) { + if (node === targetRange.range || (isReadonlyArray(targetRange.range) && targetRange.range.includes(node as Statement))) { return; } diff --git a/src/services/services.ts b/src/services/services.ts index efb12ce4b7759..44d93e544d0ba 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -283,7 +283,6 @@ import { SourceMapSource, startsWith, Statement, - stringContains, StringLiteral, StringLiteralLike, StringLiteralType, @@ -2959,7 +2958,7 @@ export function createLanguageService( } function isNodeModulesFile(path: string): boolean { - return stringContains(path, "/node_modules/"); + return path.includes("/node_modules/"); } } diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts index 657f104418fa5..76326ad5ed9f9 100644 --- a/src/services/stringCompletions.ts +++ b/src/services/stringCompletions.ts @@ -125,7 +125,6 @@ import { skipParentheses, SourceFile, startsWith, - stringContains, StringLiteralLike, StringLiteralType, stripQuotes, @@ -572,7 +571,7 @@ function directoryResult(name: string): NameAndKind { function addReplacementSpans(text: string, textStart: number, names: readonly NameAndKind[]): readonly PathCompletion[] { const span = getDirectoryFragmentTextSpan(text, textStart); const wholeSpan = text.length === 0 ? undefined : createTextSpan(textStart, text.length); - return names.map(({ name, kind, extension }): PathCompletion => Math.max(name.indexOf(directorySeparator), name.indexOf(altDirectorySeparator)) !== -1 ? { name, kind, extension, span: wholeSpan } : { name, kind, extension, span }); + return names.map(({ name, kind, extension }): PathCompletion => (name.includes(directorySeparator) || name.includes(altDirectorySeparator)) ? { name, kind, extension, span: wholeSpan } : { name, kind, extension, span }); } function getStringLiteralCompletionsFromModuleNames(sourceFile: SourceFile, node: LiteralExpression, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker, preferences: UserPreferences): readonly PathCompletion[] { @@ -979,7 +978,7 @@ function getPatternFromFirstMatchingCondition(target: unknown, conditions: reado } if (target && typeof target === "object" && !isArray(target)) { for (const condition in target) { - if (condition === "default" || conditions.indexOf(condition) > -1 || isApplicableVersionedTypesKey(conditions, condition)) { + if (condition === "default" || conditions.includes(condition) || isApplicableVersionedTypesKey(conditions, condition)) { const pattern = (target as MapLike)[condition]; return getPatternFromFirstMatchingCondition(pattern, conditions); } @@ -1001,7 +1000,7 @@ function getCompletionsForPathMapping( ): readonly NameAndKind[] { if (!endsWith(path, "*")) { // For a path mapping "foo": ["/x/y/z.ts"], add "foo" itself as a completion. - return !stringContains(path, "*") ? justPathMappingName(path, ScriptElementKind.scriptElement) : emptyArray; + return !path.includes("*") ? justPathMappingName(path, ScriptElementKind.scriptElement) : emptyArray; } const pathPrefix = path.slice(0, path.length - 1); @@ -1102,7 +1101,7 @@ function removeLeadingDirectorySeparator(path: string): string { function getAmbientModuleCompletions(fragment: string, fragmentDirectory: string | undefined, checker: TypeChecker): readonly string[] { // Get modules that the type checker picked up const ambientModules = checker.getAmbientModules().map(sym => stripQuotes(sym.name)); - const nonRelativeModuleNames = ambientModules.filter(moduleName => startsWith(moduleName, fragment) && moduleName.indexOf("*") < 0); + const nonRelativeModuleNames = ambientModules.filter(moduleName => startsWith(moduleName, fragment) && !moduleName.includes("*")); // Nested modules of the form "module-name/sub" need to be adjusted to only return the string // after the last '/' that appears in the fragment because that's where the replacement span @@ -1233,7 +1232,7 @@ const tripleSlashDirectiveFragmentRegex = /^(\/\/\/\s*= 0) { + if (this.fileName.includes("APISample")) { return; } diff --git a/src/testRunner/runner.ts b/src/testRunner/runner.ts index 396c9da080b5b..4c8151d4c3611 100644 --- a/src/testRunner/runner.ts +++ b/src/testRunner/runner.ts @@ -161,7 +161,7 @@ function handleTestConfig() { const runnerConfig = testConfig.runners || testConfig.test; if (runnerConfig && runnerConfig.length > 0) { if (testConfig.runners) { - runUnitTests = runnerConfig.indexOf("unittest") !== -1; + runUnitTests = runnerConfig.includes("unittest"); } for (const option of runnerConfig) { if (!option) { diff --git a/src/testRunner/unittests/helpers/vfs.ts b/src/testRunner/unittests/helpers/vfs.ts index e0f16f4adc1c8..31ef31e8b0948 100644 --- a/src/testRunner/unittests/helpers/vfs.ts +++ b/src/testRunner/unittests/helpers/vfs.ts @@ -60,7 +60,7 @@ export function replaceText(fs: vfs.FileSystem, path: string, oldText: string, n throw new Error(`File ${path} does not exist`); } const old = fs.readFileSync(path, "utf-8"); - if (old.indexOf(oldText) < 0) { + if (!old.includes(oldText)) { throw new Error(`Text "${oldText}" does not exist in file ${path}`); } const newContent = old.replace(oldText, newText); diff --git a/src/testRunner/unittests/moduleResolution.ts b/src/testRunner/unittests/moduleResolution.ts index 332ecb96d22e6..1d349555399fb 100644 --- a/src/testRunner/unittests/moduleResolution.ts +++ b/src/testRunner/unittests/moduleResolution.ts @@ -63,7 +63,7 @@ function runBaseline(scenario: string, baselines: readonly string[]) { describe("unittests:: moduleResolution:: Node module resolution - relative paths", () => { // node module resolution does _not_ implicitly append these extensions to an extensionless path (though will still attempt to load them if explicitly) const nonImplicitExtensions = [ts.Extension.Mts, ts.Extension.Dmts, ts.Extension.Mjs, ts.Extension.Cts, ts.Extension.Dcts, ts.Extension.Cjs]; - const autoExtensions = ts.filter(ts.supportedTSExtensionsFlat, e => nonImplicitExtensions.indexOf(e) === -1); + const autoExtensions = ts.filter(ts.supportedTSExtensionsFlat, e => !nonImplicitExtensions.includes(e)); it("load as file", () => { const baselines: string[] = []; diff --git a/src/testRunner/unittests/tsbuild/graphOrdering.ts b/src/testRunner/unittests/tsbuild/graphOrdering.ts index 0adf5d3b26903..26e87c266a644 100644 --- a/src/testRunner/unittests/tsbuild/graphOrdering.ts +++ b/src/testRunner/unittests/tsbuild/graphOrdering.ts @@ -59,7 +59,7 @@ describe("unittests:: tsbuild - graph-ordering", () => { if (!circular) { for (const dep of deps) { const child = getProjectFileName(dep[0]); - if (buildQueue.indexOf(child) < 0) continue; + if (!buildQueue.includes(child)) continue; const parent = getProjectFileName(dep[1]); assert.isAbove(buildQueue.indexOf(child), buildQueue.indexOf(parent), `Expecting child ${child} to be built after parent ${parent}`); } @@ -73,8 +73,8 @@ describe("unittests:: tsbuild - graph-ordering", () => { function writeProjects(fileSystem: vfs.FileSystem, projectNames: string[], deps: [string, string][]): string[] { const projFileNames: string[] = []; for (const dep of deps) { - if (projectNames.indexOf(dep[0]) < 0) throw new Error(`Invalid dependency - project ${dep[0]} does not exist`); - if (projectNames.indexOf(dep[1]) < 0) throw new Error(`Invalid dependency - project ${dep[1]} does not exist`); + if (!projectNames.includes(dep[0])) throw new Error(`Invalid dependency - project ${dep[0]} does not exist`); + if (!projectNames.includes(dep[1])) throw new Error(`Invalid dependency - project ${dep[1]} does not exist`); } for (const proj of projectNames) { fileSystem.mkdirpSync(`/project/${proj}`); diff --git a/src/testRunner/unittests/tsc/declarationEmit.ts b/src/testRunner/unittests/tsc/declarationEmit.ts index 87383cad99c63..097f86d3080f0 100644 --- a/src/testRunner/unittests/tsc/declarationEmit.ts +++ b/src/testRunner/unittests/tsc/declarationEmit.ts @@ -1,4 +1,3 @@ -import * as ts from "../../_namespaces/ts"; import * as Utils from "../../_namespaces/Utils"; import { verifyTscWatch, @@ -131,7 +130,7 @@ describe("unittests:: tsc:: declarationEmit::", () => { { path: `/user/username/projects/myproject/plugin-one/node_modules/plugin-two`, symLink: `/user/username/projects/myproject/plugin-two` }, libFile, ], - changeCaseFileTestPath: str => ts.stringContains(str, "/plugin-two"), + changeCaseFileTestPath: str => str.includes("/plugin-two"), }); verifyDeclarationEmit({ @@ -161,7 +160,7 @@ ${pluginOneAction()}`, { path: `/user/username/projects/myproject/plugin-one/node_modules/plugin-two`, symLink: `/temp/yarn/data/link/plugin-two` }, libFile, ], - changeCaseFileTestPath: str => ts.stringContains(str, "/plugin-two"), + changeCaseFileTestPath: str => str.includes("/plugin-two"), }); }); @@ -254,6 +253,6 @@ ${pluginOneAction()}`, }, libFile, ], - changeCaseFileTestPath: str => ts.stringContains(str, "/pkg1"), + changeCaseFileTestPath: str => str.includes("/pkg1"), }); }); diff --git a/src/testRunner/unittests/tscWatch/resolutionCache.ts b/src/testRunner/unittests/tscWatch/resolutionCache.ts index 0ab9c2bbf0c58..854f294a758ea 100644 --- a/src/testRunner/unittests/tscWatch/resolutionCache.ts +++ b/src/testRunner/unittests/tscWatch/resolutionCache.ts @@ -69,7 +69,7 @@ describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch module resolution return false; } fileExistsIsCalled = true; - assert.isTrue(fileName.indexOf("/f2.") !== -1); + assert.isTrue(fileName.includes("/f2.")); return originalFileExists.call(host, fileName); }; sys.writeFile(root.path, `import {x} from "f2"`); @@ -88,7 +88,7 @@ describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch module resolution return false; } fileExistsIsCalled = true; - assert.isTrue(fileName.indexOf("/f1.") !== -1); + assert.isTrue(fileName.includes("/f1.")); return originalFileExists.call(host, fileName); }; sys.writeFile(root.path, `import {x} from "f1"`); @@ -129,7 +129,7 @@ describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch module resolution return false; } if (!fileExistsCalledForBar) { - fileExistsCalledForBar = fileName.indexOf("/bar.") !== -1; + fileExistsCalledForBar = fileName.includes("/bar."); } return originalFileExists.call(host, fileName); @@ -187,7 +187,7 @@ describe("unittests:: tsc-watch:: resolutionCache:: tsc-watch module resolution return false; } if (!fileExistsCalledForBar) { - fileExistsCalledForBar = fileName.indexOf("/bar.") !== -1; + fileExistsCalledForBar = fileName.includes("/bar."); } return originalFileExists.call(host, fileName); }; diff --git a/src/testRunner/unittests/tsserver/typingsInstaller.ts b/src/testRunner/unittests/tsserver/typingsInstaller.ts index 1befbe4b6f155..8abd6474a9fb9 100644 --- a/src/testRunner/unittests/tsserver/typingsInstaller.ts +++ b/src/testRunner/unittests/tsserver/typingsInstaller.ts @@ -717,7 +717,7 @@ describe("unittests:: tsserver:: typingsInstaller:: General functionality", () = logger, (installer, requestId, packageNames, cb) => { let typingFiles: (File & { typings: string; })[] = []; - if (packageNames.indexOf(ts.server.typingsInstaller.typingsName("commander")) >= 0) { + if (packageNames.includes(ts.server.typingsInstaller.typingsName("commander"))) { typingFiles = [commander, jquery, lodash, cordova]; } else { @@ -1243,7 +1243,7 @@ describe("unittests:: tsserver:: typingsInstaller:: General functionality", () = logger, (installer, requestId, packageNames, cb) => { let typingFiles: (File & { typings: string; })[] = []; - if (packageNames.indexOf(ts.server.typingsInstaller.typingsName("commander")) >= 0) { + if (packageNames.includes(ts.server.typingsInstaller.typingsName("commander"))) { typingFiles = [commander]; } else { diff --git a/src/typingsInstaller/nodeTypingsInstaller.ts b/src/typingsInstaller/nodeTypingsInstaller.ts index 841e106ef925f..fcf8b61cf5baf 100644 --- a/src/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/typingsInstaller/nodeTypingsInstaller.ts @@ -10,7 +10,6 @@ import { MapLike, normalizePath, normalizeSlashes, - stringContains, sys, toPath, version, @@ -123,7 +122,7 @@ export class NodeTypingsInstaller extends TypingsInstaller { this.npmPath = npmLocation !== undefined ? npmLocation : getDefaultNPMLocation(process.argv[0], validateDefaultNpmLocation, this.installTypingHost); // If the NPM path contains spaces and isn't wrapped in quotes, do so. - if (stringContains(this.npmPath, " ") && this.npmPath[0] !== `"`) { + if (this.npmPath.includes(" ") && this.npmPath[0] !== `"`) { this.npmPath = `"${this.npmPath}"`; } if (this.log.isEnabled()) { From 51e7a34c2c1557259599d6039c3e20da05d23abe Mon Sep 17 00:00:00 2001 From: TypeScript Bot Date: Sat, 26 Aug 2023 06:22:45 +0000 Subject: [PATCH 08/12] Update package-lock.json --- package-lock.json | 90 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ac9d6ef09e27..bb6aa9c83c486 100644 --- a/package-lock.json +++ b/package-lock.json @@ -545,9 +545,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.7.0.tgz", - "integrity": "sha512-+HencqxU7CFJnQb7IKtuNBqS6Yx3Tz4kOL8BJXo+JyeiBm5MEX6pO8onXDkjrkCRlfYXS1Axro15ZjVFe9YgsA==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", + "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -577,9 +577,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1797,15 +1797,15 @@ } }, "node_modules/eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -2139,16 +2139,17 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", + "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.7", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=12.0.0" } }, "node_modules/flatted": { @@ -2627,6 +2628,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2645,6 +2652,15 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "node_modules/keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4094,9 +4110,9 @@ } }, "@eslint-community/regexpp": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.7.0.tgz", - "integrity": "sha512-+HencqxU7CFJnQb7IKtuNBqS6Yx3Tz4kOL8BJXo+JyeiBm5MEX6pO8onXDkjrkCRlfYXS1Axro15ZjVFe9YgsA==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", + "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", "dev": true }, "@eslint/eslintrc": { @@ -4117,9 +4133,9 @@ } }, "@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true }, "@humanwhocodes/config-array": { @@ -5023,15 +5039,15 @@ "dev": true }, "eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -5265,12 +5281,13 @@ "dev": true }, "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", + "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", "dev": true, "requires": { - "flatted": "^3.1.0", + "flatted": "^3.2.7", + "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, @@ -5618,6 +5635,12 @@ "argparse": "^2.0.1" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -5636,6 +5659,15 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", From 753c4638212faf2b7567036278b11339252fc79d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 26 Aug 2023 08:20:55 -0700 Subject: [PATCH 09/12] Only infer readonly tuples for `const` type parameters when constraints permit (#55229) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mateusz BurzyƄski --- src/compiler/checker.ts | 16 +- .../typeParameterConstModifiers.errors.txt | 83 +++++ .../reference/typeParameterConstModifiers.js | 125 ++++++++ .../typeParameterConstModifiers.symbols | 253 +++++++++++++++ .../typeParameterConstModifiers.types | 299 ++++++++++++++++++ .../typeParameterConstModifiers.ts | 83 +++++ 6 files changed, 854 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 21ed301c23eb6..fd69b99c1522d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23674,6 +23674,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); } + function isMutableArrayLikeType(type: Type): boolean { + // A type is mutable-array-like if it is a reference to the global Array type, or if it is not the + // any, undefined or null type and if it is assignable to Array + return isMutableArrayOrTuple(type) || !(type.flags & (TypeFlags.Any | TypeFlags.Nullable)) && isTypeAssignableTo(type, anyArrayType); + } + function getSingleBaseForNonAugmentingSubtype(type: Type) { if (!(getObjectFlags(type) & ObjectFlags.Reference) || !(getObjectFlags((type as TypeReference).target) & ObjectFlags.ClassOrInterface)) { return undefined; @@ -24338,7 +24344,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i)); } if (targetRestType) { - callback(getRestTypeAtPosition(source, paramCount), targetRestType); + callback(getRestTypeAtPosition(source, paramCount, /*readonly*/ isConstTypeVariable(targetRestType) && !someType(targetRestType, isMutableArrayLikeType)), targetRestType); } } @@ -30541,7 +30547,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return createTupleType(elementTypes, elementFlags); } if (forceTuple || inConstContext || inTupleContext) { - return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext)); + return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext && !(contextualType && someType(contextualType, isMutableArrayLikeType)))); } return createArrayLiteralType(createArrayType( elementTypes.length ? @@ -33139,7 +33145,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { names.push((arg as SyntheticExpression).tupleNameSource!); } } - return createTupleType(types, flags, inConstContext, length(names) === length(types) ? names : undefined); + return createTupleType(types, flags, inConstContext && !someType(restType, isMutableArrayLikeType), length(names) === length(types) ? names : undefined); } function checkTypeArguments(signature: Signature, typeArgumentNodes: readonly TypeNode[], reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | undefined { @@ -35455,7 +35461,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return undefined; } - function getRestTypeAtPosition(source: Signature, pos: number): Type { + function getRestTypeAtPosition(source: Signature, pos: number, readonly?: boolean): Type { const parameterCount = getParameterCount(source); const minArgumentCount = getMinArgumentCount(source); const restType = getEffectiveRestType(source); @@ -35479,7 +35485,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { names.push(name); } } - return createTupleType(types, flags, /*readonly*/ false, length(names) === length(types) ? names : undefined); + return createTupleType(types, flags, readonly, length(names) === length(types) ? names : undefined); } // Return the number of parameters in a signature. The rest parameter, if present, counts as one diff --git a/tests/baselines/reference/typeParameterConstModifiers.errors.txt b/tests/baselines/reference/typeParameterConstModifiers.errors.txt index 8a57e4b939313..3818707f74751 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.errors.txt +++ b/tests/baselines/reference/typeParameterConstModifiers.errors.txt @@ -107,4 +107,87 @@ typeParameterConstModifiers.ts(55,9): error TS1277: 'const' modifier can only ap const thingMapped = >(o: NotEmptyMapped) => o; const tMapped = thingMapped({ foo: '' }); // { foo: "" } + + // repro from https://github.com/microsoft/TypeScript/issues/55033 + + function factory_55033_minimal(cb: (...args: T) => void) { + return {} as T + } + + const test_55033_minimal = factory_55033_minimal((b: string) => {}) + + function factory_55033(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; + } + + const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" + ); + + const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" + ); + + // Same with non-readonly constraint + + function factory_55033_2(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; + } + + const t1_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" + ); + + const t2_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" + ); + + // Repro from https://github.com/microsoft/TypeScript/issues/51931 + + declare function fn(...args: T): T; + + const a = fn("a", false); + + // More examples of non-readonly constraints + + declare function fa1(args: T): T; + declare function fa2(args: T): T; + + fa1(["hello", 42]); + fa2(["hello", 42]); + + declare function fb1(...args: T): T; + declare function fb2(...args: T): T; + + fb1("hello", 42); + fb2("hello", 42); + + declare function fc1(f: (...args: T) => void, ...args: T): T; + declare function fc2(f: (...args: T) => void, ...args: T): T; + + fc1((a: string, b: number) => {}, "hello", 42); + fc2((a: string, b: number) => {}, "hello", 42); + + declare function fd1(args: T): T; + declare function fd2(args: T): T; + declare function fd3(args: T): T; + + fd1(["hello", "world"]); + fd1([1, 2, 3]); + fd2(["hello", "world"]); + fd2([1, 2, 3]); + fd3(["hello", "world"]); + fd3([1, 2, 3]); + + declare function fn1(...args: T): T; + + fn1({ foo: ["hello", 123] }, { foo: [true]}); \ No newline at end of file diff --git a/tests/baselines/reference/typeParameterConstModifiers.js b/tests/baselines/reference/typeParameterConstModifiers.js index 3fe57ad97a0e4..9c82ade3c5549 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.js +++ b/tests/baselines/reference/typeParameterConstModifiers.js @@ -101,6 +101,89 @@ type NotEmptyMapped> = keyof T extends never ? nev const thingMapped = >(o: NotEmptyMapped) => o; const tMapped = thingMapped({ foo: '' }); // { foo: "" } + +// repro from https://github.com/microsoft/TypeScript/issues/55033 + +function factory_55033_minimal(cb: (...args: T) => void) { + return {} as T +} + +const test_55033_minimal = factory_55033_minimal((b: string) => {}) + +function factory_55033(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; +} + +const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" +); + +const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" +); + +// Same with non-readonly constraint + +function factory_55033_2(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; +} + +const t1_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" +); + +const t2_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" +); + +// Repro from https://github.com/microsoft/TypeScript/issues/51931 + +declare function fn(...args: T): T; + +const a = fn("a", false); + +// More examples of non-readonly constraints + +declare function fa1(args: T): T; +declare function fa2(args: T): T; + +fa1(["hello", 42]); +fa2(["hello", 42]); + +declare function fb1(...args: T): T; +declare function fb2(...args: T): T; + +fb1("hello", 42); +fb2("hello", 42); + +declare function fc1(f: (...args: T) => void, ...args: T): T; +declare function fc2(f: (...args: T) => void, ...args: T): T; + +fc1((a: string, b: number) => {}, "hello", 42); +fc2((a: string, b: number) => {}, "hello", 42); + +declare function fd1(args: T): T; +declare function fd2(args: T): T; +declare function fd3(args: T): T; + +fd1(["hello", "world"]); +fd1([1, 2, 3]); +fd2(["hello", "world"]); +fd2([1, 2, 3]); +fd3(["hello", "world"]); +fd3([1, 2, 3]); + +declare function fn1(...args: T): T; + +fn1({ foo: ["hello", 123] }, { foo: [true]}); //// [typeParameterConstModifiers.js] @@ -154,3 +237,45 @@ var thing = function (o) { return o; }; var t = thing({ foo: '' }); // readonly { foo: "" } var thingMapped = function (o) { return o; }; var tMapped = thingMapped({ foo: '' }); // { foo: "" } +// repro from https://github.com/microsoft/TypeScript/issues/55033 +function factory_55033_minimal(cb) { + return {}; +} +var test_55033_minimal = factory_55033_minimal(function (b) { }); +function factory_55033(cb) { + return function call() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return {}; + }; +} +var t1_55033 = factory_55033(function (a, b) { })({ test: 123 }, "some string"); +var t2_55033 = factory_55033(function (a, b) { })({ test: 123 }, "some string"); +// Same with non-readonly constraint +function factory_55033_2(cb) { + return function call() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return {}; + }; +} +var t1_55033_2 = factory_55033_2(function (a, b) { })({ test: 123 }, "some string"); +var t2_55033_2 = factory_55033_2(function (a, b) { })({ test: 123 }, "some string"); +var a = fn("a", false); +fa1(["hello", 42]); +fa2(["hello", 42]); +fb1("hello", 42); +fb2("hello", 42); +fc1(function (a, b) { }, "hello", 42); +fc2(function (a, b) { }, "hello", 42); +fd1(["hello", "world"]); +fd1([1, 2, 3]); +fd2(["hello", "world"]); +fd2([1, 2, 3]); +fd3(["hello", "world"]); +fd3([1, 2, 3]); +fn1({ foo: ["hello", 123] }, { foo: [true] }); diff --git a/tests/baselines/reference/typeParameterConstModifiers.symbols b/tests/baselines/reference/typeParameterConstModifiers.symbols index 2ec51bed100f0..029008e299870 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.symbols +++ b/tests/baselines/reference/typeParameterConstModifiers.symbols @@ -361,3 +361,256 @@ const tMapped = thingMapped({ foo: '' }); // { foo: "" } >thingMapped : Symbol(thingMapped, Decl(typeParameterConstModifiers.ts, 97, 5)) >foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 99, 29)) +// repro from https://github.com/microsoft/TypeScript/issues/55033 + +function factory_55033_minimal(cb: (...args: T) => void) { +>factory_55033_minimal : Symbol(factory_55033_minimal, Decl(typeParameterConstModifiers.ts, 99, 41)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 103, 31)) +>cb : Symbol(cb, Decl(typeParameterConstModifiers.ts, 103, 67)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 103, 72)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 103, 31)) + + return {} as T +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 103, 31)) +} + +const test_55033_minimal = factory_55033_minimal((b: string) => {}) +>test_55033_minimal : Symbol(test_55033_minimal, Decl(typeParameterConstModifiers.ts, 107, 5)) +>factory_55033_minimal : Symbol(factory_55033_minimal, Decl(typeParameterConstModifiers.ts, 99, 41)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 107, 50)) + +function factory_55033(cb: (...args: T) => void) { +>factory_55033 : Symbol(factory_55033, Decl(typeParameterConstModifiers.ts, 107, 67)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 109, 23)) +>cb : Symbol(cb, Decl(typeParameterConstModifiers.ts, 109, 59)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 109, 64)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 109, 23)) + + return function call(...args: K): K { +>call : Symbol(call, Decl(typeParameterConstModifiers.ts, 110, 10)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 110, 25)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 109, 23)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 110, 44)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 110, 25)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 110, 25)) + + return {} as K; +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 110, 25)) + + }; +} + +const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( +>t1_55033 : Symbol(t1_55033, Decl(typeParameterConstModifiers.ts, 115, 5)) +>factory_55033 : Symbol(factory_55033, Decl(typeParameterConstModifiers.ts, 107, 67)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 115, 32)) +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 115, 36)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 115, 52)) + + { test: 123 }, +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 116, 5)) + + "some string" +); + +const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( +>t2_55033 : Symbol(t2_55033, Decl(typeParameterConstModifiers.ts, 120, 5)) +>factory_55033 : Symbol(factory_55033, Decl(typeParameterConstModifiers.ts, 107, 67)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 120, 32)) +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 120, 36)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 120, 52)) + + { test: 123 } as const, +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 121, 5)) +>const : Symbol(const) + + "some string" +); + +// Same with non-readonly constraint + +function factory_55033_2(cb: (...args: T) => void) { +>factory_55033_2 : Symbol(factory_55033_2, Decl(typeParameterConstModifiers.ts, 123, 2)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 127, 25)) +>cb : Symbol(cb, Decl(typeParameterConstModifiers.ts, 127, 52)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 127, 57)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 127, 25)) + + return function call(...args: K): K { +>call : Symbol(call, Decl(typeParameterConstModifiers.ts, 128, 10)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 128, 25)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 127, 25)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 128, 44)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 128, 25)) +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 128, 25)) + + return {} as K; +>K : Symbol(K, Decl(typeParameterConstModifiers.ts, 128, 25)) + + }; +} + +const t1_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( +>t1_55033_2 : Symbol(t1_55033_2, Decl(typeParameterConstModifiers.ts, 133, 5)) +>factory_55033_2 : Symbol(factory_55033_2, Decl(typeParameterConstModifiers.ts, 123, 2)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 133, 36)) +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 133, 40)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 133, 56)) + + { test: 123 }, +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 134, 5)) + + "some string" +); + +const t2_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( +>t2_55033_2 : Symbol(t2_55033_2, Decl(typeParameterConstModifiers.ts, 138, 5)) +>factory_55033_2 : Symbol(factory_55033_2, Decl(typeParameterConstModifiers.ts, 123, 2)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 138, 36)) +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 138, 40)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 138, 56)) + + { test: 123 } as const, +>test : Symbol(test, Decl(typeParameterConstModifiers.ts, 139, 5)) +>const : Symbol(const) + + "some string" +); + +// Repro from https://github.com/microsoft/TypeScript/issues/51931 + +declare function fn(...args: T): T; +>fn : Symbol(fn, Decl(typeParameterConstModifiers.ts, 141, 2)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 145, 20)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 145, 43)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 145, 20)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 145, 20)) + +const a = fn("a", false); +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 147, 5)) +>fn : Symbol(fn, Decl(typeParameterConstModifiers.ts, 141, 2)) + +// More examples of non-readonly constraints + +declare function fa1(args: T): T; +>fa1 : Symbol(fa1, Decl(typeParameterConstModifiers.ts, 147, 25)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 151, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 151, 48)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 151, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 151, 21)) + +declare function fa2(args: T): T; +>fa2 : Symbol(fa2, Decl(typeParameterConstModifiers.ts, 151, 60)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 152, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 152, 57)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 152, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 152, 21)) + +fa1(["hello", 42]); +>fa1 : Symbol(fa1, Decl(typeParameterConstModifiers.ts, 147, 25)) + +fa2(["hello", 42]); +>fa2 : Symbol(fa2, Decl(typeParameterConstModifiers.ts, 151, 60)) + +declare function fb1(...args: T): T; +>fb1 : Symbol(fb1, Decl(typeParameterConstModifiers.ts, 155, 19)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 157, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 157, 48)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 157, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 157, 21)) + +declare function fb2(...args: T): T; +>fb2 : Symbol(fb2, Decl(typeParameterConstModifiers.ts, 157, 63)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 158, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 158, 57)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 158, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 158, 21)) + +fb1("hello", 42); +>fb1 : Symbol(fb1, Decl(typeParameterConstModifiers.ts, 155, 19)) + +fb2("hello", 42); +>fb2 : Symbol(fb2, Decl(typeParameterConstModifiers.ts, 157, 63)) + +declare function fc1(f: (...args: T) => void, ...args: T): T; +>fc1 : Symbol(fc1, Decl(typeParameterConstModifiers.ts, 161, 17)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 163, 21)) +>f : Symbol(f, Decl(typeParameterConstModifiers.ts, 163, 48)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 163, 52)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 163, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 163, 72)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 163, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 163, 21)) + +declare function fc2(f: (...args: T) => void, ...args: T): T; +>fc2 : Symbol(fc2, Decl(typeParameterConstModifiers.ts, 163, 88)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 164, 21)) +>f : Symbol(f, Decl(typeParameterConstModifiers.ts, 164, 57)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 164, 61)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 164, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 164, 81)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 164, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 164, 21)) + +fc1((a: string, b: number) => {}, "hello", 42); +>fc1 : Symbol(fc1, Decl(typeParameterConstModifiers.ts, 161, 17)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 166, 5)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 166, 15)) + +fc2((a: string, b: number) => {}, "hello", 42); +>fc2 : Symbol(fc2, Decl(typeParameterConstModifiers.ts, 163, 88)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 167, 5)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 167, 15)) + +declare function fd1(args: T): T; +>fd1 : Symbol(fd1, Decl(typeParameterConstModifiers.ts, 167, 47)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 169, 58)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 169, 21)) + +declare function fd2(args: T): T; +>fd2 : Symbol(fd2, Decl(typeParameterConstModifiers.ts, 169, 70)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 170, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 170, 67)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 170, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 170, 21)) + +declare function fd3(args: T): T; +>fd3 : Symbol(fd3, Decl(typeParameterConstModifiers.ts, 170, 79)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 171, 21)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 171, 76)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 171, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 171, 21)) + +fd1(["hello", "world"]); +>fd1 : Symbol(fd1, Decl(typeParameterConstModifiers.ts, 167, 47)) + +fd1([1, 2, 3]); +>fd1 : Symbol(fd1, Decl(typeParameterConstModifiers.ts, 167, 47)) + +fd2(["hello", "world"]); +>fd2 : Symbol(fd2, Decl(typeParameterConstModifiers.ts, 169, 70)) + +fd2([1, 2, 3]); +>fd2 : Symbol(fd2, Decl(typeParameterConstModifiers.ts, 169, 70)) + +fd3(["hello", "world"]); +>fd3 : Symbol(fd3, Decl(typeParameterConstModifiers.ts, 170, 79)) + +fd3([1, 2, 3]); +>fd3 : Symbol(fd3, Decl(typeParameterConstModifiers.ts, 170, 79)) + +declare function fn1(...args: T): T; +>fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 178, 15)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 180, 21)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 180, 38)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 180, 59)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 180, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 180, 21)) + +fn1({ foo: ["hello", 123] }, { foo: [true]}); +>fn1 : Symbol(fn1, Decl(typeParameterConstModifiers.ts, 178, 15)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 182, 5)) +>foo : Symbol(foo, Decl(typeParameterConstModifiers.ts, 182, 30)) + diff --git a/tests/baselines/reference/typeParameterConstModifiers.types b/tests/baselines/reference/typeParameterConstModifiers.types index 809a64ce0b70c..5f7e230e14a25 100644 --- a/tests/baselines/reference/typeParameterConstModifiers.types +++ b/tests/baselines/reference/typeParameterConstModifiers.types @@ -409,3 +409,302 @@ const tMapped = thingMapped({ foo: '' }); // { foo: "" } >foo : "" >'' : "" +// repro from https://github.com/microsoft/TypeScript/issues/55033 + +function factory_55033_minimal(cb: (...args: T) => void) { +>factory_55033_minimal : (cb: (...args: T) => void) => T +>cb : (...args: T) => void +>args : T + + return {} as T +>{} as T : T +>{} : {} +} + +const test_55033_minimal = factory_55033_minimal((b: string) => {}) +>test_55033_minimal : readonly [b: string] +>factory_55033_minimal((b: string) => {}) : readonly [b: string] +>factory_55033_minimal : (cb: (...args: T) => void) => T +>(b: string) => {} : (b: string) => void +>b : string + +function factory_55033(cb: (...args: T) => void) { +>factory_55033 : (cb: (...args: T) => void) => (...args: K) => K +>cb : (...args: T) => void +>args : T + + return function call(...args: K): K { +>function call(...args: K): K { return {} as K; } : (...args: K) => K +>call : (...args: K) => K +>args : K + + return {} as K; +>{} as K : K +>{} : {} + + }; +} + +const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( +>t1_55033 : readonly [{ readonly test: 123; }, "some string"] +>factory_55033((a: { test: number }, b: string) => {})( { test: 123 }, "some string") : readonly [{ readonly test: 123; }, "some string"] +>factory_55033((a: { test: number }, b: string) => {}) : (...args: K) => K +>factory_55033 : (cb: (...args: T) => void) => (...args: K) => K +>(a: { test: number }, b: string) => {} : (a: { test: number;}, b: string) => void +>a : { test: number; } +>test : number +>b : string + + { test: 123 }, +>{ test: 123 } : { test: 123; } +>test : 123 +>123 : 123 + + "some string" +>"some string" : "some string" + +); + +const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( +>t2_55033 : readonly [{ readonly test: 123; }, "some string"] +>factory_55033((a: { test: number }, b: string) => {})( { test: 123 } as const, "some string") : readonly [{ readonly test: 123; }, "some string"] +>factory_55033((a: { test: number }, b: string) => {}) : (...args: K) => K +>factory_55033 : (cb: (...args: T) => void) => (...args: K) => K +>(a: { test: number }, b: string) => {} : (a: { test: number;}, b: string) => void +>a : { test: number; } +>test : number +>b : string + + { test: 123 } as const, +>{ test: 123 } as const : { readonly test: 123; } +>{ test: 123 } : { readonly test: 123; } +>test : 123 +>123 : 123 + + "some string" +>"some string" : "some string" + +); + +// Same with non-readonly constraint + +function factory_55033_2(cb: (...args: T) => void) { +>factory_55033_2 : (cb: (...args: T) => void) => (...args: K) => K +>cb : (...args: T) => void +>args : T + + return function call(...args: K): K { +>function call(...args: K): K { return {} as K; } : (...args: K) => K +>call : (...args: K) => K +>args : K + + return {} as K; +>{} as K : K +>{} : {} + + }; +} + +const t1_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( +>t1_55033_2 : [{ readonly test: 123; }, "some string"] +>factory_55033_2((a: { test: number }, b: string) => {})( { test: 123 }, "some string") : [{ readonly test: 123; }, "some string"] +>factory_55033_2((a: { test: number }, b: string) => {}) : (...args: K) => K +>factory_55033_2 : (cb: (...args: T) => void) => (...args: K) => K +>(a: { test: number }, b: string) => {} : (a: { test: number;}, b: string) => void +>a : { test: number; } +>test : number +>b : string + + { test: 123 }, +>{ test: 123 } : { test: 123; } +>test : 123 +>123 : 123 + + "some string" +>"some string" : "some string" + +); + +const t2_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( +>t2_55033_2 : [{ readonly test: 123; }, "some string"] +>factory_55033_2((a: { test: number }, b: string) => {})( { test: 123 } as const, "some string") : [{ readonly test: 123; }, "some string"] +>factory_55033_2((a: { test: number }, b: string) => {}) : (...args: K) => K +>factory_55033_2 : (cb: (...args: T) => void) => (...args: K) => K +>(a: { test: number }, b: string) => {} : (a: { test: number;}, b: string) => void +>a : { test: number; } +>test : number +>b : string + + { test: 123 } as const, +>{ test: 123 } as const : { readonly test: 123; } +>{ test: 123 } : { readonly test: 123; } +>test : 123 +>123 : 123 + + "some string" +>"some string" : "some string" + +); + +// Repro from https://github.com/microsoft/TypeScript/issues/51931 + +declare function fn(...args: T): T; +>fn : (...args: T) => T +>args : T + +const a = fn("a", false); +>a : ["a", false] +>fn("a", false) : ["a", false] +>fn : (...args: T) => T +>"a" : "a" +>false : false + +// More examples of non-readonly constraints + +declare function fa1(args: T): T; +>fa1 : (args: T) => T +>args : T + +declare function fa2(args: T): T; +>fa2 : (args: T) => T +>args : T + +fa1(["hello", 42]); +>fa1(["hello", 42]) : ["hello", 42] +>fa1 : (args: T) => T +>["hello", 42] : ["hello", 42] +>"hello" : "hello" +>42 : 42 + +fa2(["hello", 42]); +>fa2(["hello", 42]) : readonly ["hello", 42] +>fa2 : (args: T) => T +>["hello", 42] : ["hello", 42] +>"hello" : "hello" +>42 : 42 + +declare function fb1(...args: T): T; +>fb1 : (...args: T) => T +>args : T + +declare function fb2(...args: T): T; +>fb2 : (...args: T) => T +>args : T + +fb1("hello", 42); +>fb1("hello", 42) : ["hello", 42] +>fb1 : (...args: T) => T +>"hello" : "hello" +>42 : 42 + +fb2("hello", 42); +>fb2("hello", 42) : readonly ["hello", 42] +>fb2 : (...args: T) => T +>"hello" : "hello" +>42 : 42 + +declare function fc1(f: (...args: T) => void, ...args: T): T; +>fc1 : (f: (...args: T) => void, ...args: T) => T +>f : (...args: T) => void +>args : T +>args : T + +declare function fc2(f: (...args: T) => void, ...args: T): T; +>fc2 : (f: (...args: T) => void, ...args: T) => T +>f : (...args: T) => void +>args : T +>args : T + +fc1((a: string, b: number) => {}, "hello", 42); +>fc1((a: string, b: number) => {}, "hello", 42) : ["hello", 42] +>fc1 : (f: (...args: T) => void, ...args: T) => T +>(a: string, b: number) => {} : (a: string, b: number) => void +>a : string +>b : number +>"hello" : "hello" +>42 : 42 + +fc2((a: string, b: number) => {}, "hello", 42); +>fc2((a: string, b: number) => {}, "hello", 42) : readonly ["hello", 42] +>fc2 : (f: (...args: T) => void, ...args: T) => T +>(a: string, b: number) => {} : (a: string, b: number) => void +>a : string +>b : number +>"hello" : "hello" +>42 : 42 + +declare function fd1(args: T): T; +>fd1 : (args: T) => T +>args : T + +declare function fd2(args: T): T; +>fd2 : (args: T) => T +>args : T + +declare function fd3(args: T): T; +>fd3 : (args: T) => T +>args : T + +fd1(["hello", "world"]); +>fd1(["hello", "world"]) : ["hello", "world"] +>fd1 : (args: T) => T +>["hello", "world"] : ["hello", "world"] +>"hello" : "hello" +>"world" : "world" + +fd1([1, 2, 3]); +>fd1([1, 2, 3]) : [1, 2, 3] +>fd1 : (args: T) => T +>[1, 2, 3] : [1, 2, 3] +>1 : 1 +>2 : 2 +>3 : 3 + +fd2(["hello", "world"]); +>fd2(["hello", "world"]) : ["hello", "world"] +>fd2 : (args: T) => T +>["hello", "world"] : ["hello", "world"] +>"hello" : "hello" +>"world" : "world" + +fd2([1, 2, 3]); +>fd2([1, 2, 3]) : [1, 2, 3] +>fd2 : (args: T) => T +>[1, 2, 3] : [1, 2, 3] +>1 : 1 +>2 : 2 +>3 : 3 + +fd3(["hello", "world"]); +>fd3(["hello", "world"]) : readonly ["hello", "world"] +>fd3 : (args: T) => T +>["hello", "world"] : ["hello", "world"] +>"hello" : "hello" +>"world" : "world" + +fd3([1, 2, 3]); +>fd3([1, 2, 3]) : readonly [1, 2, 3] +>fd3 : (args: T) => T +>[1, 2, 3] : [1, 2, 3] +>1 : 1 +>2 : 2 +>3 : 3 + +declare function fn1(...args: T): T; +>fn1 : (...args: T) => T +>foo : unknown[] +>args : T + +fn1({ foo: ["hello", 123] }, { foo: [true]}); +>fn1({ foo: ["hello", 123] }, { foo: [true]}) : [{ readonly foo: ["hello", 123]; }, { readonly foo: [true]; }] +>fn1 : (...args: T) => T +>{ foo: ["hello", 123] } : { foo: ["hello", 123]; } +>foo : ["hello", 123] +>["hello", 123] : ["hello", 123] +>"hello" : "hello" +>123 : 123 +>{ foo: [true]} : { foo: [true]; } +>foo : [true] +>[true] : [true] +>true : true + diff --git a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts index b66b92ff7024b..14afe2219d188 100644 --- a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts +++ b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts @@ -100,3 +100,86 @@ type NotEmptyMapped> = keyof T extends never ? nev const thingMapped = >(o: NotEmptyMapped) => o; const tMapped = thingMapped({ foo: '' }); // { foo: "" } + +// repro from https://github.com/microsoft/TypeScript/issues/55033 + +function factory_55033_minimal(cb: (...args: T) => void) { + return {} as T +} + +const test_55033_minimal = factory_55033_minimal((b: string) => {}) + +function factory_55033(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; +} + +const t1_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" +); + +const t2_55033 = factory_55033((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" +); + +// Same with non-readonly constraint + +function factory_55033_2(cb: (...args: T) => void) { + return function call(...args: K): K { + return {} as K; + }; +} + +const t1_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 }, + "some string" +); + +const t2_55033_2 = factory_55033_2((a: { test: number }, b: string) => {})( + { test: 123 } as const, + "some string" +); + +// Repro from https://github.com/microsoft/TypeScript/issues/51931 + +declare function fn(...args: T): T; + +const a = fn("a", false); + +// More examples of non-readonly constraints + +declare function fa1(args: T): T; +declare function fa2(args: T): T; + +fa1(["hello", 42]); +fa2(["hello", 42]); + +declare function fb1(...args: T): T; +declare function fb2(...args: T): T; + +fb1("hello", 42); +fb2("hello", 42); + +declare function fc1(f: (...args: T) => void, ...args: T): T; +declare function fc2(f: (...args: T) => void, ...args: T): T; + +fc1((a: string, b: number) => {}, "hello", 42); +fc2((a: string, b: number) => {}, "hello", 42); + +declare function fd1(args: T): T; +declare function fd2(args: T): T; +declare function fd3(args: T): T; + +fd1(["hello", "world"]); +fd1([1, 2, 3]); +fd2(["hello", "world"]); +fd2([1, 2, 3]); +fd3(["hello", "world"]); +fd3([1, 2, 3]); + +declare function fn1(...args: T): T; + +fn1({ foo: ["hello", 123] }, { foo: [true]}); From 5b9547b1ba473a1ba234e98d35a095b7db8bd796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 26 Aug 2023 17:57:45 +0200 Subject: [PATCH 10/12] Add a fourslash completions test related to JSDoc `@template` on prototype method (#55513) --- .../jsdocTemplatePrototypeCompletions.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/cases/fourslash/jsdocTemplatePrototypeCompletions.ts diff --git a/tests/cases/fourslash/jsdocTemplatePrototypeCompletions.ts b/tests/cases/fourslash/jsdocTemplatePrototypeCompletions.ts new file mode 100644 index 0000000000000..065663f2620cb --- /dev/null +++ b/tests/cases/fourslash/jsdocTemplatePrototypeCompletions.ts @@ -0,0 +1,18 @@ +/// + +// @checkJs: true +// @filename: index.js + +//// https://github.com/microsoft/TypeScript/issues/11492 + +//// /** @constructor */ +//// function Foo() {} +//// /** +//// * @template T +//// * @param {T} bar +//// * @returns {T} +//// */ +//// Foo.prototype.foo = function (bar) {}; +//// new Foo().foo({ id: 1234 })./**/ + +verify.completions({ marker: "", exact: ["id"] }); From 270a471e119772e860ef1d2a2b32037c83136c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 26 Aug 2023 17:57:57 +0200 Subject: [PATCH 11/12] Add a test case with a generic member call returning a closed over object (#55514) --- ...lOnMemberReturningClosedOverObject.symbols | 37 +++++++++++++++++ ...allOnMemberReturningClosedOverObject.types | 41 +++++++++++++++++++ ...icCallOnMemberReturningClosedOverObject.ts | 14 +++++++ 3 files changed, 92 insertions(+) create mode 100644 tests/baselines/reference/genericCallOnMemberReturningClosedOverObject.symbols create mode 100644 tests/baselines/reference/genericCallOnMemberReturningClosedOverObject.types create mode 100644 tests/cases/compiler/genericCallOnMemberReturningClosedOverObject.ts diff --git a/tests/baselines/reference/genericCallOnMemberReturningClosedOverObject.symbols b/tests/baselines/reference/genericCallOnMemberReturningClosedOverObject.symbols new file mode 100644 index 0000000000000..9c47ba6396f68 --- /dev/null +++ b/tests/baselines/reference/genericCallOnMemberReturningClosedOverObject.symbols @@ -0,0 +1,37 @@ +//// [tests/cases/compiler/genericCallOnMemberReturningClosedOverObject.ts] //// + +=== genericCallOnMemberReturningClosedOverObject.ts === +// https://github.com/microsoft/TypeScript/issues/11584 + +function example() { +>example : Symbol(example, Decl(genericCallOnMemberReturningClosedOverObject.ts, 0, 0)) +>T1 : Symbol(T1, Decl(genericCallOnMemberReturningClosedOverObject.ts, 2, 17)) + + let x = { +>x : Symbol(x, Decl(genericCallOnMemberReturningClosedOverObject.ts, 3, 5)) + + foo: (t2: T2) => x, +>foo : Symbol(foo, Decl(genericCallOnMemberReturningClosedOverObject.ts, 3, 11)) +>T2 : Symbol(T2, Decl(genericCallOnMemberReturningClosedOverObject.ts, 4, 10)) +>t2 : Symbol(t2, Decl(genericCallOnMemberReturningClosedOverObject.ts, 4, 14)) +>T2 : Symbol(T2, Decl(genericCallOnMemberReturningClosedOverObject.ts, 4, 10)) +>x : Symbol(x, Decl(genericCallOnMemberReturningClosedOverObject.ts, 3, 5)) + + bar: (t1: T1) => x, +>bar : Symbol(bar, Decl(genericCallOnMemberReturningClosedOverObject.ts, 4, 27)) +>t1 : Symbol(t1, Decl(genericCallOnMemberReturningClosedOverObject.ts, 5, 10)) +>T1 : Symbol(T1, Decl(genericCallOnMemberReturningClosedOverObject.ts, 2, 17)) +>x : Symbol(x, Decl(genericCallOnMemberReturningClosedOverObject.ts, 3, 5)) + + }; + return x; +>x : Symbol(x, Decl(genericCallOnMemberReturningClosedOverObject.ts, 3, 5)) +} + +example().foo("hello").bar(1); +>example().foo("hello").bar : Symbol(bar, Decl(genericCallOnMemberReturningClosedOverObject.ts, 4, 27)) +>example().foo : Symbol(foo, Decl(genericCallOnMemberReturningClosedOverObject.ts, 3, 11)) +>example : Symbol(example, Decl(genericCallOnMemberReturningClosedOverObject.ts, 0, 0)) +>foo : Symbol(foo, Decl(genericCallOnMemberReturningClosedOverObject.ts, 3, 11)) +>bar : Symbol(bar, Decl(genericCallOnMemberReturningClosedOverObject.ts, 4, 27)) + diff --git a/tests/baselines/reference/genericCallOnMemberReturningClosedOverObject.types b/tests/baselines/reference/genericCallOnMemberReturningClosedOverObject.types new file mode 100644 index 0000000000000..73fe6c0db0db5 --- /dev/null +++ b/tests/baselines/reference/genericCallOnMemberReturningClosedOverObject.types @@ -0,0 +1,41 @@ +//// [tests/cases/compiler/genericCallOnMemberReturningClosedOverObject.ts] //// + +=== genericCallOnMemberReturningClosedOverObject.ts === +// https://github.com/microsoft/TypeScript/issues/11584 + +function example() { +>example : () => { foo: (t2: T2) => any; bar: (t1: T1) => any; } + + let x = { +>x : { foo: (t2: T2) => any; bar: (t1: T1) => any; } +>{ foo: (t2: T2) => x, bar: (t1: T1) => x, } : { foo: (t2: T2) => any; bar: (t1: T1) => any; } + + foo: (t2: T2) => x, +>foo : (t2: T2) => { foo: any; bar: (t1: T1) => any; } +>(t2: T2) => x : (t2: T2) => { foo: any; bar: (t1: T1) => any; } +>t2 : T2 +>x : { foo: (t2: T2) => any; bar: (t1: T1) => any; } + + bar: (t1: T1) => x, +>bar : (t1: T1) => { foo: (t2: T2) => any; bar: any; } +>(t1: T1) => x : (t1: T1) => { foo: (t2: T2) => any; bar: any; } +>t1 : T1 +>x : { foo: (t2: T2) => any; bar: (t1: T1) => any; } + + }; + return x; +>x : { foo: (t2: T2) => any; bar: (t1: T1) => any; } +} + +example().foo("hello").bar(1); +>example().foo("hello").bar(1) : { foo: (t2: T2) => any; bar: (t1: number) => any; } +>example().foo("hello").bar : (t1: number) => { foo: (t2: T2) => any; bar: any; } +>example().foo("hello") : { foo: (t2: T2) => any; bar: (t1: number) => any; } +>example().foo : (t2: T2) => { foo: any; bar: (t1: number) => any; } +>example() : { foo: (t2: T2) => any; bar: (t1: number) => any; } +>example : () => { foo: (t2: T2) => any; bar: (t1: T1) => any; } +>foo : (t2: T2) => { foo: any; bar: (t1: number) => any; } +>"hello" : "hello" +>bar : (t1: number) => { foo: (t2: T2) => any; bar: any; } +>1 : 1 + diff --git a/tests/cases/compiler/genericCallOnMemberReturningClosedOverObject.ts b/tests/cases/compiler/genericCallOnMemberReturningClosedOverObject.ts new file mode 100644 index 0000000000000..f4f39357c6df6 --- /dev/null +++ b/tests/cases/compiler/genericCallOnMemberReturningClosedOverObject.ts @@ -0,0 +1,14 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/11584 + +function example() { + let x = { + foo: (t2: T2) => x, + bar: (t1: T1) => x, + }; + return x; +} + +example().foo("hello").bar(1); From 27a5bdd44602e03764767309ede39e1a10bd474f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 26 Aug 2023 17:58:48 +0200 Subject: [PATCH 12/12] Add a test for finding all references of same-named imports from two missing modules (#55519) --- ...odulesOverlappingSpecifiers.baseline.jsonc | 43 +++++++++++++++++++ ...RefsMissingModulesOverlappingSpecifiers.ts | 7 +++ 2 files changed, 50 insertions(+) create mode 100644 tests/baselines/reference/findAllRefsMissingModulesOverlappingSpecifiers.baseline.jsonc create mode 100644 tests/cases/fourslash/findAllRefsMissingModulesOverlappingSpecifiers.ts diff --git a/tests/baselines/reference/findAllRefsMissingModulesOverlappingSpecifiers.baseline.jsonc b/tests/baselines/reference/findAllRefsMissingModulesOverlappingSpecifiers.baseline.jsonc new file mode 100644 index 0000000000000..88507edce01d9 --- /dev/null +++ b/tests/baselines/reference/findAllRefsMissingModulesOverlappingSpecifiers.baseline.jsonc @@ -0,0 +1,43 @@ +// === findAllReferences === +// === /tests/cases/fourslash/findAllRefsMissingModulesOverlappingSpecifiers.ts === +// // https://github.com/microsoft/TypeScript/issues/5551 +// import { resolve/*FIND ALL REFS*/ as resolveUrl } from "idontcare"; +// import { resolve } from "whatever"; + + + +// === findAllReferences === +// === /tests/cases/fourslash/findAllRefsMissingModulesOverlappingSpecifiers.ts === +// // https://github.com/microsoft/TypeScript/issues/5551 +// import { resolve as resolveUrl } from "idontcare"; +// <|import { [|{| isWriteAccess: true, isDefinition: true |}resolve|]/*FIND ALL REFS*/ } from "whatever";|> + + // === Definitions === + // === /tests/cases/fourslash/findAllRefsMissingModulesOverlappingSpecifiers.ts === + // // https://github.com/microsoft/TypeScript/issues/5551 + // import { resolve as resolveUrl } from "idontcare"; + // <|import { [|resolve|]/*FIND ALL REFS*/ } from "whatever";|> + + // === Details === + [ + { + "containerKind": "", + "containerName": "", + "kind": "alias", + "name": "import resolve", + "displayParts": [ + { + "text": "import", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "resolve", + "kind": "aliasName" + } + ] + } + ] \ No newline at end of file diff --git a/tests/cases/fourslash/findAllRefsMissingModulesOverlappingSpecifiers.ts b/tests/cases/fourslash/findAllRefsMissingModulesOverlappingSpecifiers.ts new file mode 100644 index 0000000000000..328e2ba835652 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsMissingModulesOverlappingSpecifiers.ts @@ -0,0 +1,7 @@ +/// + +//// // https://github.com/microsoft/TypeScript/issues/5551 +//// import { resolve/*0*/ as resolveUrl } from "idontcare"; +//// import { resolve/*1*/ } from "whatever"; + +verify.baselineFindAllReferences("0", "1");