Skip to content

Commit

Permalink
Fix unintended 'as const' name lookup error (#44311) (#44370)
Browse files Browse the repository at this point in the history
* Fix logic for methods in isTypeParameterPossiblyReferenced

* Add regression tests

Co-authored-by: Anders Hejlsberg <andersh@microsoft.com>
  • Loading branch information
DanielRosenwasser and ahejlsberg authored Jun 1, 2021
1 parent 28e3e6f commit e425f57
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 3 deletions.
8 changes: 5 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15941,8 +15941,7 @@ namespace ts {
}

function maybeTypeParameterReference(node: Node) {
return !(node.kind === SyntaxKind.QualifiedName ||
node.parent.kind === SyntaxKind.TypeReference && (<TypeReferenceNode>node.parent).typeArguments && node === (<TypeReferenceNode>node.parent).typeName ||
return !(node.parent.kind === SyntaxKind.TypeReference && (node.parent as TypeReferenceNode).typeArguments && node === (node.parent as TypeReferenceNode).typeName ||
node.parent.kind === SyntaxKind.ImportType && (node.parent as ImportTypeNode).typeArguments && node === (node.parent as ImportTypeNode).qualifier);
}

Expand Down Expand Up @@ -15971,7 +15970,10 @@ namespace ts {
return true;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return (!(node as FunctionLikeDeclaration).type && !!(node as FunctionLikeDeclaration).body) || !!forEachChild(node, containsReference);
return !(node as FunctionLikeDeclaration).type && !!(node as FunctionLikeDeclaration).body ||
some((node as FunctionLikeDeclaration).typeParameters, containsReference) ||
some((node as FunctionLikeDeclaration).parameters, containsReference) ||
!!(node as FunctionLikeDeclaration).type && containsReference((node as FunctionLikeDeclaration).type!);
}
return !!forEachChild(node, containsReference);
}
Expand Down
70 changes: 70 additions & 0 deletions tests/baselines/reference/noAsConstNameLookup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//// [noAsConstNameLookup.ts]
// Repros from #44292

type Store = { a: 123 }
export type Cleaner = <W extends Store>(runner: FeatureRunner<W>) => Promise<any>

export class FeatureRunner<W extends Store> {
private readonly cleaners: Cleaner[] = []

async runFeature(): Promise<any> {
const objectWhichShouldBeConst = {
flags: {},
settings: {},
} as const;
return objectWhichShouldBeConst
}

async run(): Promise<any> {
const result = {}
this.cleaners.forEach(c => c(this))
return result
}
}

export class C<T> {
f(): void {
let one = 1 as const;
}
}
new C<string>().f();


//// [noAsConstNameLookup.js]
// Repros from #44292
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
export class FeatureRunner {
constructor() {
this.cleaners = [];
}
runFeature() {
return __awaiter(this, void 0, void 0, function* () {
const objectWhichShouldBeConst = {
flags: {},
settings: {},
};
return objectWhichShouldBeConst;
});
}
run() {
return __awaiter(this, void 0, void 0, function* () {
const result = {};
this.cleaners.forEach(c => c(this));
return result;
});
}
}
export class C {
f() {
let one = 1;
}
}
new C().f();
81 changes: 81 additions & 0 deletions tests/baselines/reference/noAsConstNameLookup.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
=== tests/cases/compiler/noAsConstNameLookup.ts ===
// Repros from #44292

type Store = { a: 123 }
>Store : Symbol(Store, Decl(noAsConstNameLookup.ts, 0, 0))
>a : Symbol(a, Decl(noAsConstNameLookup.ts, 2, 14))

export type Cleaner = <W extends Store>(runner: FeatureRunner<W>) => Promise<any>
>Cleaner : Symbol(Cleaner, Decl(noAsConstNameLookup.ts, 2, 23))
>W : Symbol(W, Decl(noAsConstNameLookup.ts, 3, 23))
>Store : Symbol(Store, Decl(noAsConstNameLookup.ts, 0, 0))
>runner : Symbol(runner, Decl(noAsConstNameLookup.ts, 3, 40))
>FeatureRunner : Symbol(FeatureRunner, Decl(noAsConstNameLookup.ts, 3, 81))
>W : Symbol(W, Decl(noAsConstNameLookup.ts, 3, 23))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))

export class FeatureRunner<W extends Store> {
>FeatureRunner : Symbol(FeatureRunner, Decl(noAsConstNameLookup.ts, 3, 81))
>W : Symbol(W, Decl(noAsConstNameLookup.ts, 5, 27))
>Store : Symbol(Store, Decl(noAsConstNameLookup.ts, 0, 0))

private readonly cleaners: Cleaner[] = []
>cleaners : Symbol(FeatureRunner.cleaners, Decl(noAsConstNameLookup.ts, 5, 45))
>Cleaner : Symbol(Cleaner, Decl(noAsConstNameLookup.ts, 2, 23))

async runFeature(): Promise<any> {
>runFeature : Symbol(FeatureRunner.runFeature, Decl(noAsConstNameLookup.ts, 6, 45))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))

const objectWhichShouldBeConst = {
>objectWhichShouldBeConst : Symbol(objectWhichShouldBeConst, Decl(noAsConstNameLookup.ts, 9, 13))

flags: {},
>flags : Symbol(flags, Decl(noAsConstNameLookup.ts, 9, 42))

settings: {},
>settings : Symbol(settings, Decl(noAsConstNameLookup.ts, 10, 22))

} as const;
return objectWhichShouldBeConst
>objectWhichShouldBeConst : Symbol(objectWhichShouldBeConst, Decl(noAsConstNameLookup.ts, 9, 13))
}

async run(): Promise<any> {
>run : Symbol(FeatureRunner.run, Decl(noAsConstNameLookup.ts, 14, 5))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))

const result = {}
>result : Symbol(result, Decl(noAsConstNameLookup.ts, 17, 13))

this.cleaners.forEach(c => c(this))
>this.cleaners.forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>this.cleaners : Symbol(FeatureRunner.cleaners, Decl(noAsConstNameLookup.ts, 5, 45))
>this : Symbol(FeatureRunner, Decl(noAsConstNameLookup.ts, 3, 81))
>cleaners : Symbol(FeatureRunner.cleaners, Decl(noAsConstNameLookup.ts, 5, 45))
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>c : Symbol(c, Decl(noAsConstNameLookup.ts, 18, 30))
>c : Symbol(c, Decl(noAsConstNameLookup.ts, 18, 30))
>this : Symbol(FeatureRunner, Decl(noAsConstNameLookup.ts, 3, 81))

return result
>result : Symbol(result, Decl(noAsConstNameLookup.ts, 17, 13))
}
}

export class C<T> {
>C : Symbol(C, Decl(noAsConstNameLookup.ts, 21, 1))
>T : Symbol(T, Decl(noAsConstNameLookup.ts, 23, 15))

f(): void {
>f : Symbol(C.f, Decl(noAsConstNameLookup.ts, 23, 19))

let one = 1 as const;
>one : Symbol(one, Decl(noAsConstNameLookup.ts, 25, 11))
}
}
new C<string>().f();
>new C<string>().f : Symbol(C.f, Decl(noAsConstNameLookup.ts, 23, 19))
>C : Symbol(C, Decl(noAsConstNameLookup.ts, 21, 1))
>f : Symbol(C.f, Decl(noAsConstNameLookup.ts, 23, 19))

83 changes: 83 additions & 0 deletions tests/baselines/reference/noAsConstNameLookup.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
=== tests/cases/compiler/noAsConstNameLookup.ts ===
// Repros from #44292

type Store = { a: 123 }
>Store : Store
>a : 123

export type Cleaner = <W extends Store>(runner: FeatureRunner<W>) => Promise<any>
>Cleaner : Cleaner
>runner : FeatureRunner<W>

export class FeatureRunner<W extends Store> {
>FeatureRunner : FeatureRunner<W>

private readonly cleaners: Cleaner[] = []
>cleaners : Cleaner[]
>[] : never[]

async runFeature(): Promise<any> {
>runFeature : () => Promise<any>

const objectWhichShouldBeConst = {
>objectWhichShouldBeConst : { readonly flags: {}; readonly settings: {}; }
>{ flags: {}, settings: {}, } as const : { readonly flags: {}; readonly settings: {}; }
>{ flags: {}, settings: {}, } : { readonly flags: {}; readonly settings: {}; }

flags: {},
>flags : {}
>{} : {}

settings: {},
>settings : {}
>{} : {}

} as const;
return objectWhichShouldBeConst
>objectWhichShouldBeConst : { readonly flags: {}; readonly settings: {}; }
}

async run(): Promise<any> {
>run : () => Promise<any>

const result = {}
>result : {}
>{} : {}

this.cleaners.forEach(c => c(this))
>this.cleaners.forEach(c => c(this)) : void
>this.cleaners.forEach : (callbackfn: (value: Cleaner, index: number, array: Cleaner[]) => void, thisArg?: any) => void
>this.cleaners : Cleaner[]
>this : this
>cleaners : Cleaner[]
>forEach : (callbackfn: (value: Cleaner, index: number, array: Cleaner[]) => void, thisArg?: any) => void
>c => c(this) : (c: Cleaner) => Promise<any>
>c : Cleaner
>c(this) : Promise<any>
>c : Cleaner
>this : this

return result
>result : {}
}
}

export class C<T> {
>C : C<T>

f(): void {
>f : () => void

let one = 1 as const;
>one : 1
>1 as const : 1
>1 : 1
}
}
new C<string>().f();
>new C<string>().f() : void
>new C<string>().f : () => void
>new C<string>() : C<string>
>C : typeof C
>f : () => void

32 changes: 32 additions & 0 deletions tests/cases/compiler/noAsConstNameLookup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// @strict: true
// @target: es2015

// Repros from #44292

type Store = { a: 123 }
export type Cleaner = <W extends Store>(runner: FeatureRunner<W>) => Promise<any>

export class FeatureRunner<W extends Store> {
private readonly cleaners: Cleaner[] = []

async runFeature(): Promise<any> {
const objectWhichShouldBeConst = {
flags: {},
settings: {},
} as const;
return objectWhichShouldBeConst
}

async run(): Promise<any> {
const result = {}
this.cleaners.forEach(c => c(this))
return result
}
}

export class C<T> {
f(): void {
let one = 1 as const;
}
}
new C<string>().f();

0 comments on commit e425f57

Please sign in to comment.