Add a code fixer for --isolatedDeclarations errors (#58260)

Co-authored-by: Andrew Branch <andrewbranch@users.noreply.github.com>
This commit is contained in:
Hana Joo
2024-05-01 00:25:51 +02:00
committed by GitHub
parent 749bd834be
commit 33b156147b
58 changed files with 2696 additions and 6 deletions
+1
View File
@@ -1611,6 +1611,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
getBaseTypes,
getBaseTypeOfLiteralType,
getWidenedType,
getWidenedLiteralType,
getTypeFromTypeNode: nodeIn => {
const node = getParseTreeNode(nodeIn, isTypeNode);
return node ? getTypeFromTypeNode(node) : errorType;
+40
View File
@@ -7336,6 +7336,46 @@
"category": "Message",
"code": 90061
},
"Add annotation of type '{0}'": {
"category": "Message",
"code": 90062
},
"Add return type '{0}'": {
"category": "Message",
"code": 90063
},
"Extract base class to variable": {
"category": "Message",
"code": 90064
},
"Extract default export to variable": {
"category": "Message",
"code": 90065
},
"Extract binding expressions to variable": {
"category": "Message",
"code": 90066
},
"Add all missing type annotations": {
"category": "Message",
"code": 90067
},
"Add satisfies and an inline type assertion with '{0}'": {
"category": "Message",
"code": 90068
},
"Extract to variable and replace with '{0} as typeof {0}'": {
"category": "Message",
"code": 90069
},
"Mark array literal as const": {
"category": "Message",
"code": 90070
},
"Annotate types of properties expando function in a namespace": {
"category": "Message",
"code": 90071
},
"Convert function to an ES2015 class": {
"category": "Message",
+2
View File
@@ -5022,6 +5022,8 @@ export interface TypeChecker {
getBaseTypeOfLiteralType(type: Type): Type;
getWidenedType(type: Type): Type;
/** @internal */
getWidenedLiteralType(type: Type): Type;
/** @internal */
getPromisedTypeOfPromise(promise: Type, errorNode?: Node): Type | undefined;
/** @internal */
getAwaitedType(type: Type): Type | undefined;
+1
View File
@@ -50,6 +50,7 @@ export * from "../codefixes/fixUnreachableCode";
export * from "../codefixes/fixUnusedLabel";
export * from "../codefixes/fixJSDocTypes";
export * from "../codefixes/fixMissingCallParentheses";
export * from "../codefixes/fixMissingTypeAnnotationOnExports";
export * from "../codefixes/fixAwaitInSyncFunction";
export * from "../codefixes/fixPropertyOverrideAccessor";
export * from "../codefixes/inferFromUsage";
+8 -1
View File
@@ -18,6 +18,7 @@ import {
DiagnosticWithLocation,
FileTextChanges,
flatMap,
getEmitDeclarations,
isString,
map,
TextChange,
@@ -124,9 +125,15 @@ export function eachDiagnostic(context: CodeFixAllContext, errorCodes: readonly
}
function getDiagnostics({ program, sourceFile, cancellationToken }: CodeFixContextBase) {
return [
const diagnostics = [
...program.getSemanticDiagnostics(sourceFile, cancellationToken),
...program.getSyntacticDiagnostics(sourceFile, cancellationToken),
...computeSuggestionDiagnostics(sourceFile, program, cancellationToken),
];
if (getEmitDeclarations(program.getCompilerOptions())) {
diagnostics.push(
...program.getDeclarationDiagnostics(sourceFile, cancellationToken),
);
}
return diagnostics;
}
File diff suppressed because it is too large Load Diff
+2 -5
View File
@@ -61,7 +61,6 @@ import {
hasEffectiveModifier,
hasSyntacticModifier,
Identifier,
identifierToKeywordKind,
isArray,
isArrowFunction,
isAssignmentExpression,
@@ -92,7 +91,6 @@ import {
isModuleBlock,
isParenthesizedTypeNode,
isPartOfTypeNode,
isPrivateIdentifier,
isPropertyAccessExpression,
isPropertyDeclaration,
isQualifiedName,
@@ -161,6 +159,7 @@ import {
VisitResult,
} from "../_namespaces/ts";
import {
getIdentifierForNode,
refactorKindBeginsWith,
registerRefactor,
} from "../_namespaces/ts.refactor";
@@ -1374,9 +1373,7 @@ function extractConstantInScope(
// Make a unique name for the extracted variable
const file = scope.getSourceFile();
const localNameText = isPropertyAccessExpression(node) && !isClassLike(scope) && !checker.resolveName(node.name.text, node, SymbolFlags.Value, /*excludeGlobals*/ false) && !isPrivateIdentifier(node.name) && !identifierToKeywordKind(node.name)
? node.name.text
: getUniqueName(isClassLike(scope) ? "newProperty" : "newLocal", file);
const localNameText = getIdentifierForNode(node, scope, checker, file);
const isJS = isInJSFile(scope);
let variableType = isJS || !checker.isContextSensitive(node)
+22
View File
@@ -1,12 +1,22 @@
import {
ClassLikeDeclaration,
codefix,
Debug,
findAncestor,
FunctionLikeDeclaration,
getUniqueName,
identifierToKeywordKind,
isAnyImportOrRequireStatement,
isClassLike,
isPrivateIdentifier,
isPropertyAccessExpression,
ModuleBlock,
Node,
Program,
skipAlias,
SourceFile,
Symbol,
SymbolFlags,
TypeChecker,
} from "../_namespaces/ts";
import { addImportsForMovedSymbols } from "./moveToFile";
@@ -39,6 +49,18 @@ export function refactorKindBeginsWith(known: string, requested: string | undefi
return known.substr(0, requested.length) === requested;
}
/**
* Try to come up with a unique name for a given node within the scope for the
* use of being used as a property/variable name.
*
* @internal
*/
export function getIdentifierForNode(node: Node, scope: FunctionLikeDeclaration | SourceFile | ModuleBlock | ClassLikeDeclaration, checker: TypeChecker, file: SourceFile) {
return isPropertyAccessExpression(node) && !isClassLike(scope) && !checker.resolveName(node.name.text, node, SymbolFlags.Value, /*excludeGlobals*/ false) && !isPrivateIdentifier(node.name) && !identifierToKeywordKind(node.name)
? node.name.text
: getUniqueName(isClassLike(scope) ? "newProperty" : "newLocal", file);
}
/** @internal */
export function addTargetFileImports(
oldFile: SourceFile,
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////function foo() { return 42; }
////export const g = foo();
verify.codeFix({
description: "Add annotation of type 'number'",
index: 0,
newFileContent:
`function foo() { return 42; }
export const g: number = foo();`,
});
@@ -0,0 +1,22 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////function foo() {
//// return { x: 1, y: 1 };
////}
////export default foo();
verify.codeFix({
description: "Extract default export to variable",
index: 0,
newFileContent:
`function foo() {
return { x: 1, y: 1 };
}
const _default_1: {
x: number;
y: number;
} = foo();
export default _default_1;`,
});
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
//// function mixin<T extends new (...a: any) => any>(ctor: T): T {
//// return ctor;
//// }
//// class Point2D { x = 0; y = 0; }
//// export class Point3D extends mixin(Point2D) { z = 0; }
verify.codeFix({
description: ts.Diagnostics.Extract_base_class_to_variable.message,
index: 0,
newFileContent:
`function mixin<T extends new (...a: any) => any>(ctor: T): T {
return ctor;
}
class Point2D { x = 0; y = 0; }
const Point3DBase: typeof Point2D = mixin(Point2D);
export class Point3D extends Point3DBase { z = 0; }`
});
@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
//// function foo() {
//// return { x: 1, y: 1 };
//// }
//// export const { x, y } = foo();
verify.codeFix({
description: ts.Diagnostics.Extract_binding_expressions_to_variable.message,
index: 0,
newFileContent:
`function foo() {
return { x: 1, y: 1 };
}
const dest = foo();
export const x: number = dest.x;
export const y: number = dest.y;`
});
@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
//// function foo() {
//// return { x: 1, y: 1 };
//// }
//// export const { x: abcd, y: defg } = foo();
verify.codeFix({
description: ts.Diagnostics.Extract_binding_expressions_to_variable.message,
index: 0,
newFileContent:
`function foo() {
return { x: 1, y: 1 };
}
const dest = foo();
export const abcd: number = dest.x;
export const defg: number = dest.y;`
});
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
//// function foo() {
//// return { x: 1, y: 1};
//// }
//// export const { x, y = 0} = foo(), z= 42;
verify.codeFix({
description: ts.Diagnostics.Extract_binding_expressions_to_variable.message,
index: 0,
newFileContent:
`function foo() {
return { x: 1, y: 1};
}
const dest = foo();
export const x: number = dest.x;
const temp = dest.y;
export const y: number = temp === undefined ? 0 : dest.y;
export const z = 42;`});
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
//// function foo() {
//// return { x: 1, y: 1 } as const;
//// }
//// export const { x, y = 0 } = foo();
verify.codeFix({
description: ts.Diagnostics.Extract_binding_expressions_to_variable.message,
index: 0,
newFileContent:
`function foo() {
return { x: 1, y: 1 } as const;
}
const dest = foo();
export const x: 1 = dest.x;
const temp = dest.y;
export const y: 1 | 0 = temp === undefined ? 0 : dest.y;`
});
@@ -0,0 +1,28 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
//// function foo() {
//// return { x: 1, y: {42: {dd: "45"}, b: 2} };
//// }
//// function foo3(): "42" {
//// return "42";
//// }
//// export const { x: a , y: { [foo3()]: {dd: e} } } = foo();
verify.codeFix({
description: ts.Diagnostics.Extract_binding_expressions_to_variable.message,
index: 0,
newFileContent:
`function foo() {
return { x: 1, y: {42: {dd: "45"}, b: 2} };
}
function foo3(): "42" {
return "42";
}
const dest = foo();
export const a: number = dest.x;
const _a = foo3();
export const e: string = (dest.y)[_a].dd;`
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
//// export const a = Symbol();
verify.codeFix({
description: "Add annotation of type 'unique symbol'",
index: 0,
newFileContent:
`export const a: unique symbol = Symbol();`
});
@@ -0,0 +1,22 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
//// function foo() { return 42; }
//// export class A {
//// readonly a = () => foo();
//// }
verify.codeFixAvailable([
{ description: "Add return type 'number'" },
]);
verify.codeFix({
description: "Add return type 'number'",
index: 0,
newFileContent:
`function foo() { return 42; }
export class A {
readonly a = (): number => foo();
}`
});
@@ -0,0 +1,19 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
////export const a = {
//// z: Symbol()
////} as const;
verify.codeFix({
description: `Add annotation of type '{ readonly z: symbol; }'`,
index: 0,
newFileContent:
`export const a: {
readonly z: symbol;
} = {
z: Symbol()
} as const;`
});
@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////const a = 42;
////const b = 43;
////export function foo() { return a + b; }
verify.codeFixAvailable([
{ description: "Add return type 'number'" }
]);
verify.codeFix({
description: "Add return type 'number'",
index: 0,
newFileContent:
`const a = 42;
const b = 43;
export function foo(): number { return a + b; }`,
});
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
//// export function foo () {
//// return Symbol();
//// }
verify.codeFixAvailable([
{ description: "Add return type 'symbol'" }
]);
verify.codeFix({
description: "Add return type 'symbol'",
index: 0,
newFileContent:
`export function foo (): symbol {
return Symbol();
}`
});
@@ -0,0 +1,52 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
/////**
//// * Test
//// */
////export function foo(): number { return 0; }
/////**
////* Docs
////*/
////export const bar = (a = foo()) =>
//// a;
////// Trivia
verify.codeFix({
description: "Add return type 'number'",
index: 0,
applyChanges: true,
newFileContent:
`/**
* Test
*/
export function foo(): number { return 0; }
/**
* Docs
*/
export const bar = (a = foo()): number =>
a;
// Trivia`
});
verify.codeFix({
description: "Add annotation of type 'number'",
index: 0,
applyChanges: true,
newFileContent:
`/**
* Test
*/
export function foo(): number { return 0; }
/**
* Docs
*/
export const bar = (a: number = foo()): number =>
a;
// Trivia`
});
@@ -0,0 +1,22 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
/////**
//// * Test
//// */
////export function foo(){}
verify.codeFixAvailable([
{ description: "Add return type 'void'" }
]);
verify.codeFix({
description: "Add return type 'void'",
index: 0,
newFileContent:
`/**
* Test
*/
export function foo(): void{}`
});
@@ -0,0 +1,41 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////function mixin<T extends new (...a: any) => any>(ctor: T): T {
//// return ctor;
////}
////class Point2D { x = 0; y = 0; }
////interface I{}
////export class Point3D extends
//// /** Base class */
//// mixin(Point2D)
//// // Test
//// implements I
//// {
//// z = 0;
////}
verify.codeFixAvailable([
{ description: ts.Diagnostics.Extract_base_class_to_variable.message }
]);
verify.codeFix({
description: ts.Diagnostics.Extract_base_class_to_variable.message,
index: 0,
newFileContent:
`function mixin<T extends new (...a: any) => any>(ctor: T): T {
return ctor;
}
class Point2D { x = 0; y = 0; }
interface I{}
const Point3DBase: typeof Point2D =
/** Base class */
mixin(Point2D);
export class Point3D extends Point3DBase
// Test
implements I
{
z = 0;
}`
});
@@ -0,0 +1,29 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////function mixin<T extends new (...a: any) => any>(ctor: T): T {
//// return ctor;
////}
////class Point2D { x = 0; y = 0; }
////export class Point3D2 extends mixin(Point2D) {
//// z = 0;
////}
verify.codeFixAvailable([
{ description: ts.Diagnostics.Extract_base_class_to_variable.message }
]);
verify.codeFix({
description: ts.Diagnostics.Extract_base_class_to_variable.message,
index: 0,
newFileContent:
`function mixin<T extends new (...a: any) => any>(ctor: T): T {
return ctor;
}
class Point2D { x = 0; y = 0; }
const Point3D2Base: typeof Point2D = mixin(Point2D);
export class Point3D2 extends Point3D2Base {
z = 0;
}`
});
@@ -0,0 +1,29 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////function mixin<T extends new (...a: any) => any>(ctor: T): T {
//// return ctor;
////}
////class Point2D { x = 0; y = 0; }
////export class Point3D3 extends mixin(Point2D) /* DD*/ {
//// z = 0;
////}
verify.codeFixAvailable([
{ description: ts.Diagnostics.Extract_base_class_to_variable.message }
]);
verify.codeFix({
description: ts.Diagnostics.Extract_base_class_to_variable.message,
index: 0,
newFileContent:
`function mixin<T extends new (...a: any) => any>(ctor: T): T {
return ctor;
}
class Point2D { x = 0; y = 0; }
const Point3D3Base: typeof Point2D = mixin(Point2D) /* DD*/;
export class Point3D3 extends Point3D3Base {
z = 0;
}`
});
@@ -0,0 +1,31 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// fileName: code.ts
////export const extensions = {
//// /**
//// */
//// fn: <T>(actualValue: T, expectedValue: T) => {
//// return actualValue === expectedValue
//// },
//// fn2: function<T>(actualValue: T, expectedValue: T) {
//// return actualValue === expectedValue
//// }
////}
verify.codeFixAll({
fixId: "fixMissingTypeAnnotationOnExports",
fixAllDescription: ts.Diagnostics.Add_all_missing_type_annotations.message,
newFileContent:
`export const extensions = {
/**
*/
fn: <T>(actualValue: T, expectedValue: T): boolean => {
return actualValue === expectedValue
},
fn2: function<T>(actualValue: T, expectedValue: T): boolean {
return actualValue === expectedValue
}
}`
})
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// fileName: code.ts
////let p = { x: 1, y: 2}
////const a = 1, b = 10, { x, y } = p, c = 1;
////export { x, y }
////export const d = a + b + c;
verify.codeFixAll({
fixId: "fixMissingTypeAnnotationOnExports",
fixAllDescription: ts.Diagnostics.Add_all_missing_type_annotations.message,
newFileContent:
`let p = { x: 1, y: 2}
const x: number = p.x;
const y: number = p.y;
const a = 1, b = 10, c = 1;
export { x, y }
export const d: number = a + b + c;`
})
@@ -0,0 +1,111 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// fileName: code.ts
////export const sessionLoader = {
//// async loadSession() {
//// if (Math.random() > 0.5) {
//// return {
//// PROP_1: {
//// name: false,
//// },
//// PROPERTY_2: {
//// name: 1,
//// },
//// PROPERTY_3: {
//// name: 1
//// },
//// PROPERTY_4: {
//// name: 315,
//// },
//// };
//// }
////
//// return {
//// PROP_1: {
//// name: false,
//// },
//// PROPERTY_2: {
//// name: undefined,
//// },
//// PROPERTY_3: {
//// },
//// PROPERTY_4: {
//// name: 576,
//// },
//// };
//// },
////};
const description = "Add return type 'Promise<{\n PROP_1: {\n name: boolean;\n };\n PROPERTY_2: {\n name: number;\n };\n PROPERTY_3: {\n name: number;\n };\n PROPE...'";
verify.codeFixAvailable([
{ description }
]);
verify.codeFix({
description,
index: 0,
newFileContent:
`export const sessionLoader = {
async loadSession(): Promise<{
PROP_1: {
name: boolean;
};
PROPERTY_2: {
name: number;
};
PROPERTY_3: {
name: number;
};
PROPERTY_4: {
name: number;
};
} | {
PROP_1: {
name: boolean;
};
PROPERTY_2: {
name: any;
};
PROPERTY_3: {
name?: undefined;
};
PROPERTY_4: {
name: number;
};
}> {
if (Math.random() > 0.5) {
return {
PROP_1: {
name: false,
},
PROPERTY_2: {
name: 1,
},
PROPERTY_3: {
name: 1
},
PROPERTY_4: {
name: 315,
},
};
}
return {
PROP_1: {
name: false,
},
PROPERTY_2: {
name: undefined,
},
PROPERTY_3: {
},
PROPERTY_4: {
name: 576,
},
};
},
};`
});
@@ -0,0 +1,23 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// fileName: code.ts
////function getString() {
//// return ""
////}
////export const exp = {
//// prop: getString()
////};
verify.codeFix({
description: "Add satisfies and an inline type assertion with 'string'",
index: 1,
newFileContent:
`function getString() {
return ""
}
export const exp = {
prop: getString() satisfies string as string
};`
});
@@ -0,0 +1,22 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////const a = 42;
////const b = 42;
////export class C {
//// //making sure comments are not changed
//// property =a+b; // comment should stay here
////}
verify.codeFix({
description: "Add annotation of type 'number'",
index: 0,
newFileContent:
`const a = 42;
const b = 42;
export class C {
//making sure comments are not changed
property: number =a+b; // comment should stay here
}`,
});
@@ -0,0 +1,28 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @Filename: /person-code.ts
////export type Person = { x: string; }
////export function getPerson() : Person {
//// return null!
////}
// @Filename: /code.ts
////import { getPerson } from "./person-code";
////export const exp = {
//// person: getPerson()
////};
goTo.file("/code.ts");
verify.codeFix({
description: "Add satisfies and an inline type assertion with 'Person'",
index: 1,
newFileContent:
`import { getPerson, Person } from "./person-code";
export const exp = {
person: getPerson() satisfies Person as Person
};`
});
@@ -0,0 +1,39 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @Filename: /person-code.ts
////export type Person = { x: string; }
////export function getPerson() : Person {
//// return null!
////}
// @Filename: /code.ts
////import { getPerson } from "./person-code";
////export default {
//// person: getPerson()
////};
goTo.file("/code.ts");
verify.codeFixAvailable([
{
"description": "Extract default export to variable"
},
{
"description": "Add satisfies and an inline type assertion with 'Person'"
},
{
"description": "Extract to variable and replace with 'newLocal as typeof newLocal'"
}
])
verify.codeFix({
description: "Add satisfies and an inline type assertion with 'Person'",
index: 1,
newFileContent:
`import { getPerson, Person } from "./person-code";
export default {
person: getPerson() satisfies Person as Person
};`
});
@@ -0,0 +1,29 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @Filename: /code.ts
////const x = 1;
////export default {
//// x
////};
verify.codeFix({
description: "Add satisfies and an inline type assertion with 'number'",
index: 1,
newFileContent:
`const x = 1;
export default {
x: x as number
};`
});
verify.codeFix({
description: "Add satisfies and an inline type assertion with 'typeof x'",
index: 2,
newFileContent:
`const x = 1;
export default {
x: x as typeof x
};`
});
@@ -0,0 +1,26 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @Filename: /code.ts
////export class Foo {
//// m() {
//// }
////}
verify.codeFixAvailable([
{
"description": "Add return type 'void'"
},
])
verify.codeFix({
description: "Add return type 'void'",
index: 0,
newFileContent:
`export class Foo {
m(): void {
}
}`
});
@@ -0,0 +1,65 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @Filename: /code.ts
////const Start = {
//// A: 'A',
//// B: 'B',
////} as const;
////
////const End = {
//// Y: "Y",
//// Z: "Z"
////} as const;
////export const All_Part1 = {};
////function getPart() {
//// return { M: "Z"}
////}
////
////export const All = {
//// x: 1,
//// ...Start,
//// y: 1,
//// ...getPart(),
//// ...End,
//// z: 1,
////};
verify.codeFix({
description: "Add annotation of type 'typeof All_Part1_1 & typeof Start & typeof All_Part3 & typeof All_Part4 & typeof End & typeof All_Part6'" ,
index: 1,
newFileContent:
`const Start = {
A: 'A',
B: 'B',
} as const;
const End = {
Y: "Y",
Z: "Z"
} as const;
export const All_Part1 = {};
function getPart() {
return { M: "Z"}
}
const All_Part1_1 = {
x: 1
};
const All_Part3 = {
y: 1
};
const All_Part4 = getPart();
const All_Part6 = {
z: 1
};
export const All: typeof All_Part1_1 & typeof Start & typeof All_Part3 & typeof All_Part4 & typeof End & typeof All_Part6 = {
...All_Part1_1,
...Start,
...All_Part3,
...All_Part4,
...End,
...All_Part6
};`
});
@@ -0,0 +1,16 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @Filename: /code.ts
////const foo = { a: 1 }
////export const exported = foo;
verify.codeFix({
description: "Add annotation of type 'typeof foo'" ,
index: 1,
newFileContent:
`const foo = { a: 1 }
export const exported: typeof foo = foo;`
});
@@ -0,0 +1,37 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @Filename: /code.ts
////const A = "A"
////const B = "B"
////export const AB = Math.random()? A: B;
verify.codeFixAvailable([
{
"description": "Add annotation of type '\"A\" | \"B\"'"
},
{
"description": "Add annotation of type 'typeof A | typeof B'"
},
{
"description": "Add annotation of type 'string'"
},
{
"description": "Add satisfies and an inline type assertion with '\"A\" | \"B\"'"
},
{
"description": "Add satisfies and an inline type assertion with 'typeof A | typeof B'"
},
{
"description": "Add satisfies and an inline type assertion with 'string'"
}
])
verify.codeFix({
description: "Add satisfies and an inline type assertion with 'typeof A | typeof B'" ,
index: 4,
newFileContent:
`const A = "A"
const B = "B"
export const AB = (Math.random() ? A : B) satisfies typeof A | typeof B as typeof A | typeof B;`
});
@@ -0,0 +1,72 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @Filename: /code.ts
////const Start = [
//// 'A',
//// 'B',
////] as const;
////
////const End = [
//// "Y",
//// "Z"
////] as const;
////export const All_Part1 = {};
////function getPart() {
//// return ["Z"]
////}
////
////export const All = [
//// 1,
//// ...Start,
//// 1,
//// ...getPart(),
//// ...End,
//// 1,
////] as const;
verify.codeFix({
description: `Add annotation of type '[...typeof All_Part1_1, ...typeof Start, ...typeof All_Part3, ...typeof All_Part4, ...typeof End, ...typeof All_Part6]'` ,
index: 1,
newFileContent:
`const Start = [
'A',
'B',
] as const;
const End = [
"Y",
"Z"
] as const;
export const All_Part1 = {};
function getPart() {
return ["Z"]
}
const All_Part1_1 = [
1
] as const;
const All_Part3 = [
1
] as const;
const All_Part4 = getPart() as const;
const All_Part6 = [
1
] as const;
export const All: [
...typeof All_Part1_1,
...typeof Start,
...typeof All_Part3,
...typeof All_Part4,
...typeof End,
...typeof All_Part6
] = [
...All_Part1_1,
...Start,
...All_Part3,
...All_Part4,
...End,
...All_Part6
] as const;`
});
@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
////const u: unique symbol = Symbol();
////export const fn = () => ({ u } as const);
verify.codeFix({
description:
`Add return type '{ readonly u: typeof u; }'` ,
index: 0,
newFileContent:
`const u: unique symbol = Symbol();
export const fn = (): {
readonly u: typeof u;
} => ({ u } as const);`
});
@@ -0,0 +1,54 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
////let c: string[] = [];
////export let o = {
//// p: [
//// ...c
//// ]
////}
verify.codeFix({
description: `Mark array literal as const`,
applyChanges: true,
index: 2,
newFileContent:
`let c: string[] = [];
export let o = {
p: [
...c
] as const
}`
});
verify.codeFix({
description: `Extract to variable and replace with 'newLocal as typeof newLocal'`,
applyChanges: true,
index: 1,
newFileContent:
`let c: string[] = [];
const newLocal = [
...c
] as const;
export let o = {
p: newLocal as typeof newLocal
}`
});
verify.codeFix({
description: `Add annotation of type 'readonly string[]'`,
applyChanges: true,
index: 0,
newFileContent:
`let c: string[] = [];
const newLocal: readonly string[] = [
...c
] as const;
export let o = {
p: newLocal as typeof newLocal
}`
});
@@ -0,0 +1,25 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////const a = 42;
////const b = 42;
////export class C {
//// method() { return a + b };
////}
verify.codeFixAvailable([
{ description: "Add return type 'number'" },
]);
verify.codeFix({
description: "Add return type 'number'",
index: 0,
newFileContent:
`const a = 42;
const b = 42;
export class C {
method(): number { return a + b };
}`,
});
@@ -0,0 +1,42 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
////let c: string[] = [];
////export let o = {
//// p: Math.random() ? []: [
//// ...c
//// ]
////}
verify.codeFix({
description: `Extract to variable and replace with 'newLocal as typeof newLocal'`,
applyChanges: true,
index: 2,
newFileContent:
`let c: string[] = [];
const newLocal = Math.random() ? [] : [
...c
];
export let o = {
p: newLocal as typeof newLocal
}`
});
verify.codeFix({
description: `Add annotation of type 'string[]'`,
applyChanges: true,
index: 0,
newFileContent:
`let c: string[] = [];
const newLocal: string[] = Math.random() ? [] : [
...c
];
export let o = {
p: newLocal as typeof newLocal
}`
});
@@ -0,0 +1,11 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
////enum E {
//// A = "foo".length
////}
verify.codeFixAvailable([])
@@ -0,0 +1,18 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
////class A {
//// static readonly p1 = Symbol();
////}
verify.codeFix({
description: "Add annotation of type 'unique symbol'",
index: 0,
newFileContent:
`class A {
static readonly p1: unique symbol = Symbol();
}`
});
@@ -0,0 +1,24 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
////const foo = () => {}
////foo/*a*/["a"] = "A";
////foo["b"] = "C"
verify.codeFix({
description: "Add annotation of type '{ (): void; a: string; b: string; }'",
index: 1,
newFileContent:
`const foo: {
(): void;
a: string;
b: string;
} = () => {}
foo["a"] = "A";
foo["b"] = "C"`
});
@@ -0,0 +1,23 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
////function foo(): void {}
////foo.x = 1;
////foo.y = 1;
verify.codeFix({
description: "Annotate types of properties expando function in a namespace",
index: 0,
newFileContent:
`function foo(): void {}
declare namespace foo {
export var x: number;
export var y: number;
}
foo.x = 1;
foo.y = 1;`
});
@@ -0,0 +1,24 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
////function foo(): void {}
////// cannot name this property because it's an invalid variable name.
////foo["@bar"] = 42;
////foo.x = 1;
verify.codeFix({
description: "Annotate types of properties expando function in a namespace",
index: 0,
newFileContent:
`function foo(): void {}
declare namespace foo {
export var x: number;
}
// cannot name this property because it's an invalid variable name.
foo["@bar"] = 42;
foo.x = 1;`
});
@@ -0,0 +1,30 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
////function foo(): void {}
////// x already exists, so do not generate code for 'x'
////foo.x = 1;
////foo.y = 1;
////namespace foo {
//// export let x = 42;
////}
verify.codeFix({
description: "Annotate types of properties expando function in a namespace",
index: 0,
newFileContent:
`function foo(): void {}
declare namespace foo {
export var y: number;
}
// x already exists, so do not generate code for 'x'
foo.x = 1;
foo.y = 1;
namespace foo {
export let x = 42;
}`
});
@@ -0,0 +1,23 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
////const foo = (): void => {}
////foo.a = "A";
////foo.b = "C"
verify.codeFix({
description: "Add annotation of type '{ (): void; a: string; b: string; }'",
index: 0,
newFileContent:
`const foo: {
(): void;
a: string;
b: string;
} = (): void => {}
foo.a = "A";
foo.b = "C"`
});
@@ -0,0 +1,16 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @lib: es2019
// @Filename: /code.ts
//// export default 1 + 1;
verify.codeFix({
description: "Extract default export to variable",
index: 0,
newFileContent:
`const _default_1: number = 1 + 1;
export default _default_1;`
});
@@ -0,0 +1,67 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @Filename: /code.ts
//// function classDecorator<T extends Function> (value: T, context: ClassDecoratorContext) {}
//// function methodDecorator<This> (
//// target: (...args: number[])=> number,
//// context: ClassMethodDecoratorContext<This, (this: This, ...args: number[]) => number>) {}
//// function getterDecorator(value: Function, context: ClassGetterDecoratorContext) {}
//// function setterDecorator(value: Function, context: ClassSetterDecoratorContext) {}
//// function fieldDecorator(value: undefined, context: ClassFieldDecoratorContext) {}
//// function foo() { return 42;}
////
//// @classDecorator
//// export class A {
//// @methodDecorator
//// sum(...args: number[]) {
//// return args.reduce((a, b) => a + b, 0);
//// }
//// getSelf() {
//// return this;
//// }
//// @getterDecorator
//// get a() {
//// return foo();
//// }
//// @setterDecorator
//// set a(value) {}
////
//// @fieldDecorator classProp = foo();
//// }
verify.codeFixAll({
fixId: "fixMissingTypeAnnotationOnExports",
fixAllDescription: ts.Diagnostics.Add_all_missing_type_annotations.message,
newFileContent:
`function classDecorator<T extends Function> (value: T, context: ClassDecoratorContext) {}
function methodDecorator<This> (
target: (...args: number[])=> number,
context: ClassMethodDecoratorContext<This, (this: This, ...args: number[]) => number>) {}
function getterDecorator(value: Function, context: ClassGetterDecoratorContext) {}
function setterDecorator(value: Function, context: ClassSetterDecoratorContext) {}
function fieldDecorator(value: undefined, context: ClassFieldDecoratorContext) {}
function foo() { return 42;}
@classDecorator
export class A {
@methodDecorator
sum(...args: number[]): number {
return args.reduce((a, b) => a + b, 0);
}
getSelf(): this {
return this;
}
@getterDecorator
get a(): number {
return foo();
}
@setterDecorator
set a(value) {}
@fieldDecorator classProp: number = foo();
}`
});
@@ -0,0 +1,66 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
// @experimentalDecorators: true
// @Filename: /code.ts
//// function classDecorator<T extends Function>() { return (target: T) => target; }
//// function methodDecorator() { return (target: any, key: string, descriptor: PropertyDescriptor) => descriptor;}
//// function parameterDecorator() { return (target: any, key: string, idx: number) => {};}
//// function getterDecorator() { return (target: any, key: string) => {}; }
//// function setterDecorator() { return (target: any, key: string) => {}; }
//// function fieldDecorator() { return (target: any, key: string) => {}; }
//// function foo() { return 42; }
////
//// @classDecorator()
//// export class A {
//// @methodDecorator()
//// sum(...args: number[]) {
//// return args.reduce((a, b) => a + b, 0);
//// }
//// getSelf() {
//// return this;
//// }
//// passParameter(@parameterDecorator() param = foo()) {}
//// @getterDecorator()
//// get a() {
//// return foo();
//// }
//// @setterDecorator()
//// set a(value) {}
//// @fieldDecorator() classProp = foo();
//// }
verify.codeFixAll({
fixId: "fixMissingTypeAnnotationOnExports",
fixAllDescription: ts.Diagnostics.Add_all_missing_type_annotations.message,
newFileContent:
`function classDecorator<T extends Function>() { return (target: T) => target; }
function methodDecorator() { return (target: any, key: string, descriptor: PropertyDescriptor) => descriptor;}
function parameterDecorator() { return (target: any, key: string, idx: number) => {};}
function getterDecorator() { return (target: any, key: string) => {}; }
function setterDecorator() { return (target: any, key: string) => {}; }
function fieldDecorator() { return (target: any, key: string) => {}; }
function foo() { return 42; }
@classDecorator()
export class A {
@methodDecorator()
sum(...args: number[]): number {
return args.reduce((a, b) => a + b, 0);
}
getSelf(): this {
return this;
}
passParameter(@parameterDecorator() param: number = foo()): void {}
@getterDecorator()
get a(): number {
return foo();
}
@setterDecorator()
set a(value) {}
@fieldDecorator() classProp: number = foo();
}`
});
@@ -0,0 +1,24 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////const a = 42;
////const b = 42;
////export class C {
//// get property() { return a + b; }
////}
verify.codeFixAvailable([
{ description: "Add return type 'number'" }
]);
verify.codeFix({
description: "Add return type 'number'",
index: 0,
newFileContent:
`const a = 42;
const b = 42;
export class C {
get property(): number { return a + b; }
}`,
});
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////function foo(): number[] { return [42]; }
////export const c = [...foo()];
verify.codeFix({
description: "Add annotation of type 'number[]'",
index: 0,
newFileContent:
`function foo(): number[] { return [42]; }
export const c: number[] = [...foo()];`,
});
@@ -0,0 +1,16 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////function foo(): number[] { return [42]; }
////export const c = { foo: foo() };
verify.codeFix({
description: `Add annotation of type '{ foo: number[]; }'`,
index: 0,
newFileContent:
`function foo(): number[] { return [42]; }
export const c: {
foo: number[];
} = { foo: foo() };`,
});
@@ -0,0 +1,18 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////function foo() {return 42;}
////export const g = function () { return foo(); };
verify.codeFixAvailable([
{ description: "Add return type 'number'" },
]);
verify.codeFix({
description: "Add return type 'number'",
index: 0,
newFileContent:
`function foo() {return 42;}
export const g = function (): number { return foo(); };`,
});
@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts'/>
// @isolatedDeclarations: true
// @declaration: true
////function foo( ){
//// return 42;
////}
////const a = foo();
////export = a;
verify.codeFix({
description: "Add annotation of type 'number'",
index: 0,
newFileContent:
`function foo( ){
return 42;
}
const a: number = foo();
export = a;`,
});