Add codefix to generate types for untyped module (#26588)

This commit is contained in:
Andy 2018-09-18 11:47:29 -07:00 committed by GitHub
parent 7852cf7ed3
commit c57ff087d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 2117 additions and 116 deletions

View File

@ -75,6 +75,7 @@
"gulp-typescript": "latest",
"istanbul": "latest",
"jake": "latest",
"lodash": "4.17.10",
"merge2": "latest",
"minimist": "latest",
"mkdirp": "latest",

View File

@ -1409,9 +1409,12 @@ namespace ts {
/**
* Tests whether a value is string
*/
export function isString(text: any): text is string {
export function isString(text: unknown): text is string {
return typeof text === "string";
}
export function isNumber(x: unknown): x is number {
return typeof x === "number";
}
export function tryCast<TOut extends TIn, TIn = any>(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut | undefined;
export function tryCast<T>(value: T, test: (value: T) => boolean): T | undefined;
@ -1534,6 +1537,7 @@ namespace ts {
* Every function should be assignable to this, but this should not be assignable to every function.
*/
export type AnyFunction = (...args: never[]) => void;
export type AnyConstructor = new (...args: unknown[]) => unknown;
export namespace Debug {
export let currentAssertionLevel = AssertionLevel.None;
@ -2125,4 +2129,8 @@ namespace ts {
deleted(oldItems[oldIndex++]);
}
}
export function fill<T>(length: number, cb: (index: number) => T): T[] {
return new Array(length).fill(0).map((_, i) => cb(i));
}
}

View File

@ -4671,5 +4671,13 @@
"Convert all to async functions": {
"category": "Message",
"code": 95066
},
"Generate types for '{0}'": {
"category": "Message",
"code": 95067
},
"Generate types for all packages without types": {
"category": "Message",
"code": 95068
}
}
}

View File

@ -235,7 +235,7 @@ namespace ts {
// Modifiers
export function createModifier<T extends Modifier["kind"]>(kind: T) {
export function createModifier<T extends Modifier["kind"]>(kind: T): Token<T> {
return createToken(kind);
}

View File

@ -0,0 +1,159 @@
/* @internal */
namespace ts {
export interface InspectValueOptions {
readonly fileNameToRequire: string;
}
export const enum ValueKind { Const, Array, FunctionOrClass, Object }
export interface ValueInfoBase {
readonly name: string;
}
export type ValueInfo = ValueInfoSimple | ValueInfoArray | ValueInfoFunctionOrClass | ValueInfoObject;
export interface ValueInfoSimple extends ValueInfoBase {
readonly kind: ValueKind.Const;
readonly typeName: string;
readonly comment?: string | undefined;
}
export interface ValueInfoFunctionOrClass extends ValueInfoBase {
readonly kind: ValueKind.FunctionOrClass;
readonly source: string | number; // For a native function, this is the length.
readonly prototypeMembers: ReadonlyArray<ValueInfo>;
readonly namespaceMembers: ReadonlyArray<ValueInfo>;
}
export interface ValueInfoArray extends ValueInfoBase {
readonly kind: ValueKind.Array;
readonly inner: ValueInfo;
}
export interface ValueInfoObject extends ValueInfoBase {
readonly kind: ValueKind.Object;
readonly members: ReadonlyArray<ValueInfo>;
}
export function inspectModule(fileNameToRequire: string): ValueInfo {
return inspectValue(removeFileExtension(getBaseFileName(fileNameToRequire)), tryRequire(fileNameToRequire));
}
export function inspectValue(name: string, value: unknown): ValueInfo {
return getValueInfo(name, value, getRecurser());
}
type Recurser = <T>(obj: unknown, name: string, cbOk: () => T, cbFail: (isCircularReference: boolean, keyStack: ReadonlyArray<string>) => T) => T;
function getRecurser(): Recurser {
const seen = new Set<unknown>();
const nameStack: string[] = [];
return (obj, name, cbOk, cbFail) => {
if (seen.has(obj) || nameStack.length > 4) {
return cbFail(seen.has(obj), nameStack);
}
seen.add(obj);
nameStack.push(name);
const res = cbOk();
nameStack.pop();
seen.delete(obj);
return res;
};
}
function getValueInfo(name: string, value: unknown, recurser: Recurser): ValueInfo {
return recurser(value, name,
(): ValueInfo => {
if (typeof value === "function") return getFunctionOrClassInfo(value as AnyFunction, name, recurser);
if (typeof value === "object") {
const builtin = getBuiltinType(name, value as object, recurser);
if (builtin !== undefined) return builtin;
const entries = getEntriesOfObject(value as object);
return { kind: ValueKind.Object, name, members: flatMap(entries, ({ key, value }) => getValueInfo(key, value, recurser)) };
}
return { kind: ValueKind.Const, name, typeName: isNullOrUndefined(value) ? "any" : typeof value };
},
(isCircularReference, keyStack) => anyValue(name, ` ${isCircularReference ? "Circular reference" : "Too-deep object hierarchy"} from ${keyStack.join(".")}`));
}
function getFunctionOrClassInfo(fn: AnyFunction, name: string, recurser: Recurser): ValueInfoFunctionOrClass {
const prototypeMembers = getPrototypeMembers(fn, recurser);
const namespaceMembers = flatMap(getEntriesOfObject(fn), ({ key, value }) => getValueInfo(key, value, recurser));
const toString = cast(Function.prototype.toString.call(fn), isString);
const source = stringContains(toString, "{ [native code] }") ? getFunctionLength(fn) : toString;
return { kind: ValueKind.FunctionOrClass, name, source, namespaceMembers, prototypeMembers };
}
const builtins: () => ReadonlyMap<AnyConstructor> = memoize(() => {
const map = createMap<AnyConstructor>();
for (const { key, value } of getEntriesOfObject(global)) {
if (typeof value === "function" && typeof value.prototype === "object" && value !== Object) {
map.set(key, value as AnyConstructor);
}
}
return map;
});
function getBuiltinType(name: string, value: object, recurser: Recurser): ValueInfo | undefined {
return isArray(value)
? { name, kind: ValueKind.Array, inner: value.length && getValueInfo("element", first(value), recurser) || anyValue(name) }
: forEachEntry(builtins(), (builtin, builtinName): ValueInfo | undefined =>
value instanceof builtin ? { kind: ValueKind.Const, name, typeName: builtinName } : undefined);
}
function getPrototypeMembers(fn: AnyFunction, recurser: Recurser): ReadonlyArray<ValueInfo> {
const prototype = fn.prototype as unknown;
// tslint:disable-next-line no-unnecessary-type-assertion (TODO: update LKG and it will really be unnecessary)
return typeof prototype !== "object" || prototype === null ? emptyArray : mapDefined(getEntriesOfObject(prototype as object), ({ key, value }) =>
key === "constructor" ? undefined : getValueInfo(key, value, recurser));
}
const ignoredProperties: ReadonlySet<string> = new Set(["arguments", "caller", "constructor", "eval", "super_"]);
const reservedFunctionProperties: ReadonlySet<string> = new Set(Object.getOwnPropertyNames(noop));
interface ObjectEntry { readonly key: string; readonly value: unknown; }
function getEntriesOfObject(obj: object): ReadonlyArray<ObjectEntry> {
const seen = createMap<true>();
const entries: ObjectEntry[] = [];
let chain = obj;
while (!isNullOrUndefined(chain) && chain !== Object.prototype && chain !== Function.prototype) {
for (const key of Object.getOwnPropertyNames(chain)) {
if (!isJsPrivate(key) &&
!ignoredProperties.has(key) &&
(typeof obj !== "function" || !reservedFunctionProperties.has(key)) &&
// Don't add property from a higher prototype if it already exists in a lower one
addToSeen(seen, key)) {
const value = safeGetPropertyOfObject(chain, key);
// Don't repeat "toString" that matches signature from Object.prototype
if (!(key === "toString" && typeof value === "function" && value.length === 0)) {
entries.push({ key, value });
}
}
}
chain = Object.getPrototypeOf(chain);
}
return entries.sort((e1, e2) => compareStringsCaseSensitive(e1.key, e2.key));
}
function getFunctionLength(fn: AnyFunction): number {
return tryCast(safeGetPropertyOfObject(fn, "length"), isNumber) || 0;
}
function safeGetPropertyOfObject(obj: object, key: string): unknown {
const desc = Object.getOwnPropertyDescriptor(obj, key);
return desc && desc.value;
}
function isNullOrUndefined(value: unknown): value is null | undefined {
return value == null; // tslint:disable-line
}
function anyValue(name: string, comment?: string): ValueInfo {
return { kind: ValueKind.Const, name, typeName: "any", comment };
}
export function isJsPrivate(name: string): boolean {
return name.startsWith("_");
}
function tryRequire(fileNameToRequire: string): unknown {
try {
return require(fileNameToRequire);
}
catch {
return undefined;
}
}
}

View File

@ -779,14 +779,23 @@ namespace ts {
*/
/* @internal */
export function resolveJSModule(moduleName: string, initialDir: string, host: ModuleResolutionHost): string {
const { resolvedModule, failedLookupLocations } =
nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true);
const { resolvedModule, failedLookupLocations } = tryResolveJSModuleWorker(moduleName, initialDir, host);
if (!resolvedModule) {
throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations.join(", ")}`);
}
return resolvedModule.resolvedFileName;
}
/* @internal */
export function tryResolveJSModule(moduleName: string, initialDir: string, host: ModuleResolutionHost): string | undefined {
const { resolvedModule } = tryResolveJSModuleWorker(moduleName, initialDir, host);
return resolvedModule && resolvedModule.resolvedFileName;
}
function tryResolveJSModuleWorker(moduleName: string, initialDir: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
return nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true);
}
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations {
return nodeModuleNameResolverWorker(moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, /*jsOnly*/ false);
}

View File

@ -52,6 +52,7 @@
"resolutionCache.ts",
"moduleSpecifiers.ts",
"watch.ts",
"tsbuild.ts"
"tsbuild.ts",
"inspectValue.ts",
]
}

View File

@ -2502,9 +2502,7 @@ Actual: ${stringify(fullActual)}`);
const { changes, commands } = this.languageService.getCombinedCodeFix({ type: "file", fileName: this.activeFile.fileName }, fixId, this.formatCodeSettings, ts.emptyOptions);
assert.deepEqual<ReadonlyArray<{}> | undefined>(commands, expectedCommands);
assert(changes.every(c => c.fileName === this.activeFile.fileName), "TODO: support testing codefixes that touch multiple files");
this.applyChanges(changes);
this.verifyCurrentFileContent(newFileContent);
this.verifyNewContent({ newFileContent }, changes);
}
/**
@ -3389,6 +3387,19 @@ Actual: ${stringify(fullActual)}`);
private getApplicableRefactorsWorker(positionOrRange: number | ts.TextRange, fileName: string, preferences = ts.emptyOptions): ReadonlyArray<ts.ApplicableRefactorInfo> {
return this.languageService.getApplicableRefactors(fileName, positionOrRange, preferences) || ts.emptyArray;
}
public generateTypes(examples: ReadonlyArray<FourSlashInterface.GenerateTypesOptions>): void {
for (const { name = "example", value, output, outputBaseline } of examples) {
const actual = ts.generateTypesForModule(name, value, this.formatCodeSettings);
if (outputBaseline) {
if (actual === undefined) throw ts.Debug.fail();
Harness.Baseline.runBaseline(ts.combinePaths("generateTypes", outputBaseline + ts.Extension.Dts), actual);
}
else {
assert.equal(actual, output, `generateTypes output for ${name} does not match`);
}
}
}
}
function updateTextRangeForTextChanges({ pos, end }: ts.TextRange, textChanges: ReadonlyArray<ts.TextChange>): ts.TextRange {
@ -3442,7 +3453,7 @@ Actual: ${stringify(fullActual)}`);
// Parse out the files and their metadata
const testData = parseTestData(absoluteBasePath, content, absoluteFileName);
const state = new TestState(absoluteBasePath, testType, testData);
const output = ts.transpileModule(content, { reportDiagnostics: true });
const output = ts.transpileModule(content, { reportDiagnostics: true, compilerOptions: { target: ts.ScriptTarget.ES2015 } });
if (output.diagnostics!.length > 0) {
throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics![0].messageText}`);
}
@ -4512,6 +4523,18 @@ namespace FourSlashInterface {
public noMoveToNewFile(): void {
this.state.noMoveToNewFile();
}
public generateTypes(...options: GenerateTypesOptions[]): void {
this.state.generateTypes(options);
}
}
export interface GenerateTypesOptions {
readonly name?: string;
readonly value: unknown;
// Exactly one of these should be set:
readonly output?: string;
readonly outputBaseline?: string;
}
export class Edit {
@ -4901,7 +4924,7 @@ namespace FourSlashInterface {
export interface VerifyCodeFixAllOptions {
fixId: string;
fixAllDescription: string;
newFileContent: string;
newFileContent: NewFileContent;
commands: ReadonlyArray<{}>;
}

View File

@ -4,6 +4,7 @@ namespace ts.server {
export const ActionSet: ActionSet = "action::set";
export const ActionInvalidate: ActionInvalidate = "action::invalidate";
export const ActionPackageInstalled: ActionPackageInstalled = "action::packageInstalled";
export const ActionValueInspected: ActionValueInspected = "action::valueInspected";
export const EventTypesRegistry: EventTypesRegistry = "event::typesRegistry";
export const EventBeginInstallTypes: EventBeginInstallTypes = "event::beginInstallTypes";
export const EventEndInstallTypes: EventEndInstallTypes = "event::endInstallTypes";

View File

@ -2,6 +2,7 @@ declare namespace ts.server {
export type ActionSet = "action::set";
export type ActionInvalidate = "action::invalidate";
export type ActionPackageInstalled = "action::packageInstalled";
export type ActionValueInspected = "action::valueInspected";
export type EventTypesRegistry = "event::typesRegistry";
export type EventBeginInstallTypes = "event::beginInstallTypes";
export type EventEndInstallTypes = "event::endInstallTypes";
@ -12,7 +13,7 @@ declare namespace ts.server {
}
export interface TypingInstallerResponse {
readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | ActionPackageInstalled | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed;
readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | ActionPackageInstalled | ActionValueInspected | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed;
}
export interface TypingInstallerRequestWithProjectName {
@ -20,7 +21,7 @@ declare namespace ts.server {
}
/* @internal */
export type TypingInstallerRequestUnion = DiscoverTypings | CloseProject | TypesRegistryRequest | InstallPackageRequest;
export type TypingInstallerRequestUnion = DiscoverTypings | CloseProject | TypesRegistryRequest | InstallPackageRequest | InspectValueRequest;
export interface DiscoverTypings extends TypingInstallerRequestWithProjectName {
readonly fileNames: string[];
@ -47,6 +48,12 @@ declare namespace ts.server {
readonly projectRootPath: Path;
}
/* @internal */
export interface InspectValueRequest {
readonly kind: "inspectValue";
readonly options: InspectValueOptions;
}
/* @internal */
export interface TypesRegistryResponse extends TypingInstallerResponse {
readonly kind: EventTypesRegistry;
@ -59,6 +66,12 @@ declare namespace ts.server {
readonly message: string;
}
/* @internal */
export interface InspectValueResponse {
readonly kind: ActionValueInspected;
readonly result: ValueInfo;
}
export interface InitializationFailedResponse extends TypingInstallerResponse {
readonly kind: EventInitializationFailed;
readonly message: string;
@ -106,5 +119,5 @@ declare namespace ts.server {
}
/* @internal */
export type TypingInstallerResponseUnion = SetTypings | InvalidateCachedTypings | TypesRegistryResponse | PackageInstalledResponse | InstallTypes | InitializationFailedResponse;
export type TypingInstallerResponseUnion = SetTypings | InvalidateCachedTypings | TypesRegistryResponse | PackageInstalledResponse | InspectValueResponse | InstallTypes | InitializationFailedResponse;
}

View File

@ -254,6 +254,11 @@ namespace ts.server {
installPackage(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult> {
return this.typingsCache.installPackage({ ...options, projectName: this.projectName, projectRootPath: this.toPath(this.currentDirectory) });
}
/* @internal */
inspectValue(options: InspectValueOptions): Promise<ValueInfo> {
return this.typingsCache.inspectValue(options);
}
private get typingsCache(): TypingsCache {
return this.projectService.typingsCache;
}
@ -352,6 +357,10 @@ namespace ts.server {
return this.projectService.host.readFile(fileName);
}
writeFile(fileName: string, content: string): void {
return this.projectService.host.writeFile(fileName, content);
}
fileExists(file: string): boolean {
// As an optimization, don't hit the disks for files we already know don't exist
// (because we're watching for their creation).

View File

@ -1829,8 +1829,8 @@ namespace ts.server {
private applyCodeActionCommand(args: protocol.ApplyCodeActionCommandRequestArgs): {} {
const commands = args.command as CodeActionCommand | CodeActionCommand[]; // They should be sending back the command we sent them.
for (const command of toArray(commands)) {
const { project } = this.getFileAndProject(command);
project.getLanguageService().applyCodeActionCommand(command).then(
const { file, project } = this.getFileAndProject(command);
project.getLanguageService().applyCodeActionCommand(command, this.getFormatOptions(file)).then(
_result => { /* TODO: GH#20447 report success message? */ },
_error => { /* TODO: GH#20447 report errors */ });
}

View File

@ -8,6 +8,8 @@ namespace ts.server {
export interface ITypingsInstaller {
isKnownTypesPackageName(name: string): boolean;
installPackage(options: InstallPackageOptionsWithProject): Promise<ApplyCodeActionCommandResult>;
/* @internal */
inspectValue(options: InspectValueOptions): Promise<ValueInfo>;
enqueueInstallTypingsRequest(p: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray<string> | undefined): void;
attach(projectService: ProjectService): void;
onProjectClosed(p: Project): void;
@ -18,6 +20,7 @@ namespace ts.server {
isKnownTypesPackageName: returnFalse,
// Should never be called because we never provide a types registry.
installPackage: notImplemented,
inspectValue: notImplemented,
enqueueInstallTypingsRequest: noop,
attach: noop,
onProjectClosed: noop,
@ -95,6 +98,10 @@ namespace ts.server {
return this.installer.installPackage(options);
}
inspectValue(options: InspectValueOptions): Promise<ValueInfo> {
return this.installer.inspectValue(options);
}
enqueueInstallTypingsForProject(project: Project, unresolvedImports: SortedReadonlyArray<string> | undefined, forceRefresh: boolean) {
const typeAcquisition = project.getTypeAcquisition();

View File

@ -1,6 +1,9 @@
/* @internal */
namespace ts.codefix {
const fixId = "fixCannotFindModule";
const fixName = "fixCannotFindModule";
const fixIdInstallTypesPackage = "installTypesPackage";
const fixIdGenerateTypes = "generateTypes";
const errorCodeCannotFindModule = Diagnostics.Cannot_find_module_0.code;
const errorCodes = [
errorCodeCannotFindModule,
@ -10,26 +13,141 @@ namespace ts.codefix {
errorCodes,
getCodeActions: context => {
const { host, sourceFile, span: { start } } = context;
const packageName = getTypesPackageNameToInstall(host, sourceFile, start, context.errorCode);
return packageName === undefined ? []
: [createCodeFixAction(fixId, /*changes*/ [], [Diagnostics.Install_0, packageName], fixId, Diagnostics.Install_all_missing_types_packages, getCommand(sourceFile.fileName, packageName))];
const packageName = tryGetImportedPackageName(sourceFile, start);
if (packageName === undefined) return undefined;
const typesPackageName = getTypesPackageNameToInstall(packageName, host, context.errorCode);
return typesPackageName === undefined
? singleElementArray(tryGetGenerateTypesAction(context, packageName))
: [createCodeFixAction(fixName, /*changes*/ [], [Diagnostics.Install_0, typesPackageName], fixIdInstallTypesPackage, Diagnostics.Install_all_missing_types_packages, getInstallCommand(sourceFile.fileName, typesPackageName))];
},
fixIds: [fixIdInstallTypesPackage, fixIdGenerateTypes],
getAllCodeActions: context => {
let savedTypesDir: string | null | undefined = null; // tslint:disable-line no-null-keyword
return codeFixAll(context, errorCodes, (changes, diag, commands) => {
const packageName = tryGetImportedPackageName(diag.file, diag.start);
if (packageName === undefined) return undefined;
switch (context.fixId) {
case fixIdInstallTypesPackage: {
const pkg = getTypesPackageNameToInstall(packageName, context.host, diag.code);
if (pkg) {
commands.push(getInstallCommand(diag.file.fileName, pkg));
}
break;
}
case fixIdGenerateTypes: {
const typesDir = savedTypesDir !== null ? savedTypesDir : savedTypesDir = getOrCreateTypesDirectory(changes, context);
const command = typesDir === undefined ? undefined : tryGenerateTypes(typesDir, packageName, context);
if (command) commands.push(command);
break;
}
default:
Debug.fail(`Bad fixId: ${context.fixId}`);
}
});
},
fixIds: [fixId],
getAllCodeActions: context => codeFixAll(context, errorCodes, (_, diag, commands) => {
const pkg = getTypesPackageNameToInstall(context.host, diag.file, diag.start, diag.code);
if (pkg) {
commands.push(getCommand(diag.file.fileName, pkg));
}
}),
});
function getCommand(fileName: string, packageName: string): InstallPackageAction {
function tryGetGenerateTypesAction(context: CodeFixContextBase, packageName: string): CodeFixAction | undefined {
let command: GenerateTypesAction | undefined;
const changes = textChanges.ChangeTracker.with(context, t => {
const typesDir = getOrCreateTypesDirectory(t, context);
command = typesDir === undefined ? undefined : tryGenerateTypes(typesDir, packageName, context);
});
return command && createCodeFixAction(fixName, changes, [Diagnostics.Generate_types_for_0, packageName], fixIdGenerateTypes, Diagnostics.Generate_types_for_all_packages_without_types, command);
}
function tryGenerateTypes(typesDir: string, packageName: string, context: CodeFixContextBase): GenerateTypesAction | undefined {
const file = context.sourceFile.fileName;
const fileToGenerateTypesFor = tryResolveJSModule(packageName, getDirectoryPath(file), context.host as ModuleResolutionHost); // TODO: GH#18217
if (fileToGenerateTypesFor === undefined) return undefined;
const outputFileName = resolvePath(getDirectoryPath(context.program.getCompilerOptions().configFile!.fileName), typesDir, packageName + ".d.ts");
if (context.host.fileExists!(outputFileName)) return undefined;
return { type: "generate types", file, fileToGenerateTypesFor, outputFileName };
}
// If no types directory exists yet, adds it to tsconfig.json
function getOrCreateTypesDirectory(changes: textChanges.ChangeTracker, context: CodeFixContextBase): string | undefined {
const { configFile } = context.program.getCompilerOptions();
if (!configFile) return undefined;
const tsconfigObjectLiteral = getTsConfigObjectLiteralExpression(configFile);
if (!tsconfigObjectLiteral) return undefined;
const compilerOptionsProperty = findProperty(tsconfigObjectLiteral, "compilerOptions");
if (!compilerOptionsProperty) {
const newCompilerOptions = createObjectLiteral([makeDefaultBaseUrl(), makeDefaultPaths()]);
changes.insertNodeAtObjectStart(configFile, tsconfigObjectLiteral, createJsonPropertyAssignment("compilerOptions", newCompilerOptions));
return defaultTypesDirectoryName;
}
const compilerOptions = compilerOptionsProperty.initializer;
if (!isObjectLiteralExpression(compilerOptions)) return defaultTypesDirectoryName;
const baseUrl = getOrAddBaseUrl(changes, configFile, compilerOptions);
const typesDirectoryFromPathMapping = getOrAddPathMapping(changes, configFile, compilerOptions);
return combinePaths(baseUrl, typesDirectoryFromPathMapping);
}
const defaultBaseUrl = ".";
function makeDefaultBaseUrl(): PropertyAssignment {
return createJsonPropertyAssignment("baseUrl", createStringLiteral(defaultBaseUrl));
}
function getOrAddBaseUrl(changes: textChanges.ChangeTracker, tsconfig: TsConfigSourceFile, compilerOptions: ObjectLiteralExpression): string {
const baseUrlProp = findProperty(compilerOptions, "baseUrl");
if (baseUrlProp) {
return isStringLiteral(baseUrlProp.initializer) ? baseUrlProp.initializer.text : defaultBaseUrl;
}
else {
changes.insertNodeAtObjectStart(tsconfig, compilerOptions, makeDefaultBaseUrl());
return defaultBaseUrl;
}
}
const defaultTypesDirectoryName = "types";
function makeDefaultPathMapping(): PropertyAssignment {
return createJsonPropertyAssignment("*", createArrayLiteral([createStringLiteral(`${defaultTypesDirectoryName}/*`)]));
}
function makeDefaultPaths(): PropertyAssignment {
return createJsonPropertyAssignment("paths", createObjectLiteral([makeDefaultPathMapping()]));
}
function getOrAddPathMapping(changes: textChanges.ChangeTracker, tsconfig: TsConfigSourceFile, compilerOptions: ObjectLiteralExpression) {
const paths = findProperty(compilerOptions, "paths");
if (!paths || !isObjectLiteralExpression(paths.initializer)) {
changes.insertNodeAtObjectStart(tsconfig, compilerOptions, makeDefaultPaths());
return defaultTypesDirectoryName;
}
// Look for an existing path mapping. Should look like `"*": "foo/*"`.
const existing = firstDefined(paths.initializer.properties, prop =>
isPropertyAssignment(prop) && isStringLiteral(prop.name) && prop.name.text === "*" && isArrayLiteralExpression(prop.initializer)
? firstDefined(prop.initializer.elements, value => isStringLiteral(value) ? tryRemoveSuffix(value.text, "/*") : undefined)
: undefined);
if (existing) return existing;
changes.insertNodeAtObjectStart(tsconfig, paths.initializer, makeDefaultPathMapping());
return defaultTypesDirectoryName;
}
function createJsonPropertyAssignment(name: string, initializer: Expression) {
return createPropertyAssignment(createStringLiteral(name), initializer);
}
function findProperty(obj: ObjectLiteralExpression, name: string): PropertyAssignment | undefined {
return find(obj.properties, (p): p is PropertyAssignment => isPropertyAssignment(p) && !!p.name && isStringLiteral(p.name) && p.name.text === name);
}
function getInstallCommand(fileName: string, packageName: string): InstallPackageAction {
return { type: "install package", file: fileName, packageName };
}
function getTypesPackageNameToInstall(host: LanguageServiceHost, sourceFile: SourceFile, pos: number, diagCode: number): string | undefined {
function tryGetImportedPackageName(sourceFile: SourceFile, pos: number): string | undefined {
const moduleName = cast(getTokenAtPosition(sourceFile, pos), isStringLiteral).text;
const { packageName } = parsePackageName(moduleName);
return isExternalModuleNameRelative(packageName) ? undefined : packageName;
}
function getTypesPackageNameToInstall(packageName: string, host: LanguageServiceHost, diagCode: number): string | undefined {
return diagCode === errorCodeCannotFindModule
? (JsTyping.nodeCoreModules.has(packageName) ? "@types/node" : undefined)
: (host.isKnownTypesPackageName!(packageName) ? getTypesPackageName(packageName) : undefined); // TODO: GH#18217

View File

@ -0,0 +1,227 @@
/* @internal */
namespace ts {
export function generateTypesForModule(name: string, moduleValue: unknown, formatSettings: FormatCodeSettings): string {
return valueInfoToDeclarationFileText(inspectValue(name, moduleValue), formatSettings);
}
export function valueInfoToDeclarationFileText(valueInfo: ValueInfo, formatSettings: FormatCodeSettings): string {
return textChanges.getNewFileText(toStatements(valueInfo, OutputKind.ExportEquals), ScriptKind.TS, "\n", formatting.getFormatContext(formatSettings));
}
const enum OutputKind { ExportEquals, NamedExport, NamespaceMember }
function toNamespaceMemberStatements(info: ValueInfo): ReadonlyArray<Statement> {
return toStatements(info, OutputKind.NamespaceMember);
}
function toStatements(info: ValueInfo, kind: OutputKind): ReadonlyArray<Statement> {
const isDefault = info.name === InternalSymbolName.Default;
const name = isDefault ? "_default" : info.name;
if (!isValidIdentifier(name) || isDefault && kind !== OutputKind.NamedExport) return emptyArray;
const modifiers = isDefault && info.kind === ValueKind.FunctionOrClass ? [createModifier(SyntaxKind.ExportKeyword), createModifier(SyntaxKind.DefaultKeyword)]
: kind === OutputKind.ExportEquals ? [createModifier(SyntaxKind.DeclareKeyword)]
: kind === OutputKind.NamedExport ? [createModifier(SyntaxKind.ExportKeyword)]
: undefined;
const exportEquals = () => kind === OutputKind.ExportEquals ? [exportEqualsOrDefault(info.name, /*isExportEquals*/ true)] : emptyArray;
const exportDefault = () => isDefault ? [exportEqualsOrDefault("_default", /*isExportEquals*/ false)] : emptyArray;
switch (info.kind) {
case ValueKind.FunctionOrClass:
return [...exportEquals(), ...functionOrClassToStatements(modifiers, name, info)];
case ValueKind.Object:
const { members } = info;
if (kind === OutputKind.ExportEquals) {
return flatMap(members, v => toStatements(v, OutputKind.NamedExport));
}
if (members.some(m => m.kind === ValueKind.FunctionOrClass)) {
// If some member is a function, use a namespace so it gets a FunctionDeclaration or ClassDeclaration.
return [...exportDefault(), createNamespace(modifiers, name, flatMap(members, toNamespaceMemberStatements))];
}
// falls through
case ValueKind.Const:
case ValueKind.Array: {
const comment = info.kind === ValueKind.Const ? info.comment : undefined;
const constVar = createVariableStatement(modifiers, createVariableDeclarationList([createVariableDeclaration(name, toType(info))], NodeFlags.Const));
return [...exportEquals(), ...exportDefault(), addComment(constVar, comment)];
}
default:
return Debug.assertNever(info);
}
}
function exportEqualsOrDefault(name: string, isExportEquals: boolean): ExportAssignment {
return createExportAssignment(/*decorators*/ undefined, /*modifiers*/ undefined, isExportEquals, createIdentifier(name));
}
function functionOrClassToStatements(modifiers: Modifiers, name: string, { source, prototypeMembers, namespaceMembers }: ValueInfoFunctionOrClass): ReadonlyArray<Statement> {
const fnAst = parseClassOrFunctionBody(source);
const { parameters, returnType } = fnAst === undefined ? { parameters: emptyArray, returnType: anyType() } : getParametersAndReturnType(fnAst);
const instanceProperties = typeof fnAst === "object" ? getConstructorFunctionInstanceProperties(fnAst) : emptyArray;
const classStaticMembers: ClassElement[] | undefined =
instanceProperties.length !== 0 || prototypeMembers.length !== 0 || fnAst === undefined || typeof fnAst !== "number" && fnAst.kind === SyntaxKind.Constructor ? [] : undefined;
const namespaceStatements = flatMap(namespaceMembers, info => {
if (!isValidIdentifier(info.name)) return undefined;
if (classStaticMembers) {
switch (info.kind) {
case ValueKind.Object:
if (info.members.some(m => m.kind === ValueKind.FunctionOrClass)) {
break;
}
// falls through
case ValueKind.Array:
case ValueKind.Const:
classStaticMembers.push(
addComment(
createProperty(/*decorators*/ undefined, [createModifier(SyntaxKind.StaticKeyword)], info.name, /*questionOrExclamationToken*/ undefined, toType(info), /*initializer*/ undefined),
info.kind === ValueKind.Const ? info.comment : undefined));
return undefined;
case ValueKind.FunctionOrClass:
if (!info.namespaceMembers.length) { // Else, can't merge a static method with a namespace. Must make it a function on the namespace.
const sig = tryGetMethod(info, [createModifier(SyntaxKind.StaticKeyword)]);
if (sig) {
classStaticMembers.push(sig);
return undefined;
}
}
}
}
return toStatements(info, OutputKind.NamespaceMember);
});
const decl = classStaticMembers
? createClassDeclaration(
/*decorators*/ undefined,
modifiers,
name,
/*typeParameters*/ undefined,
/*heritageClauses*/ undefined,
[
...classStaticMembers,
...(parameters.length ? [createConstructor(/*decorators*/ undefined, /*modifiers*/ undefined, parameters, /*body*/ undefined)] : emptyArray),
...instanceProperties,
// ignore non-functions on the prototype
...mapDefined(prototypeMembers, info => info.kind === ValueKind.FunctionOrClass ? tryGetMethod(info) : undefined),
])
: createFunctionDeclaration(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, name, /*typeParameters*/ undefined, parameters, returnType, /*body*/ undefined);
return [decl, ...(namespaceStatements.length === 0 ? emptyArray : [createNamespace(modifiers && modifiers.map(m => getSynthesizedDeepClone(m)), name, namespaceStatements)])];
}
function tryGetMethod({ name, source }: ValueInfoFunctionOrClass, modifiers?: Modifiers): MethodDeclaration | undefined {
if (!isValidIdentifier(name)) return undefined;
const fnAst = parseClassOrFunctionBody(source);
if (fnAst === undefined || (typeof fnAst !== "number" && fnAst.kind === SyntaxKind.Constructor)) return undefined;
const sig = getParametersAndReturnType(fnAst);
return sig && createMethod(
/*decorators*/ undefined,
modifiers,
/*asteriskToken*/ undefined,
name,
/*questionToken*/ undefined,
/*typeParameters*/ undefined,
sig.parameters,
sig.returnType,
/*body*/ undefined);
}
function toType(info: ValueInfo): TypeNode {
switch (info.kind) {
case ValueKind.Const:
return createTypeReferenceNode(info.typeName, /*typeArguments*/ undefined);
case ValueKind.Array:
return createArrayTypeNode(toType(info.inner));
case ValueKind.FunctionOrClass:
return createTypeReferenceNode("Function", /*typeArguments*/ undefined); // Normally we create a FunctionDeclaration, but this can happen for a function in an array.
case ValueKind.Object:
return createTypeLiteralNode(info.members.map(m => createPropertySignature(/*modifiers*/ undefined, m.name, /*questionToken*/ undefined, toType(m), /*initializer*/ undefined)));
default:
return Debug.assertNever(info);
}
}
// Parses assignments to "this.x" in the constructor into class property declarations
function getConstructorFunctionInstanceProperties(fnAst: FunctionOrConstructorNode): ReadonlyArray<PropertyDeclaration> {
const members: PropertyDeclaration[] = [];
forEachOwnNodeOfFunction(fnAst, node => {
if (isAssignmentExpression(node, /*excludeCompoundAssignment*/ true) &&
isPropertyAccessExpression(node.left) && node.left.expression.kind === SyntaxKind.ThisKeyword) {
const name = node.left.name.text;
if (!isJsPrivate(name)) members.push(createProperty(/*decorators*/ undefined, /*modifiers*/ undefined, name, /*questionOrExclamationToken*/ undefined, anyType(), /*initializer*/ undefined));
}
});
return members;
}
interface ParametersAndReturnType { readonly parameters: ReadonlyArray<ParameterDeclaration>; readonly returnType: TypeNode; }
function getParametersAndReturnType(fnAst: FunctionOrConstructor): ParametersAndReturnType {
if (typeof fnAst === "number") {
return { parameters: fill(fnAst, i => makeParameter(`p${i}`, anyType())), returnType: anyType() };
}
let usedArguments = false, hasReturn = false;
forEachOwnNodeOfFunction(fnAst, node => {
usedArguments = usedArguments || isIdentifier(node) && node.text === "arguments";
hasReturn = hasReturn || isReturnStatement(node) && !!node.expression && node.expression.kind !== SyntaxKind.VoidExpression;
});
const parameters = [
...fnAst.parameters.map(p => makeParameter(`${p.name.getText()}`, inferParameterType(fnAst, p))),
...(usedArguments ? [makeRestParameter()] : emptyArray),
];
return { parameters, returnType: hasReturn ? anyType() : createKeywordTypeNode(SyntaxKind.VoidKeyword) };
}
function makeParameter(name: string, type: TypeNode): ParameterDeclaration {
return createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, name, /*questionToken*/ undefined, type);
}
function makeRestParameter(): ParameterDeclaration {
return createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, createToken(SyntaxKind.DotDotDotToken), "args", /*questionToken*/ undefined, createArrayTypeNode(anyType()));
}
type FunctionOrConstructorNode = FunctionExpression | ArrowFunction | ConstructorDeclaration | MethodDeclaration;
type FunctionOrConstructor = FunctionOrConstructorNode | number; // number is for native function
/** Returns 'undefined' for class with no declared constructor */
function parseClassOrFunctionBody(source: string | number): FunctionOrConstructor | undefined {
if (typeof source === "number") return source;
const classOrFunction = tryCast(parseExpression(source), (node): node is FunctionExpression | ArrowFunction | ClassExpression => isFunctionExpression(node) || isArrowFunction(node) || isClassExpression(node));
return classOrFunction
? isClassExpression(classOrFunction) ? find(classOrFunction.members, isConstructorDeclaration) : classOrFunction
// If that didn't parse, it's a method `m() {}`. Parse again inside of an object literal.
: cast(first(cast(parseExpression(`{ ${source} }`), isObjectLiteralExpression).properties), isMethodDeclaration);
}
function parseExpression(expr: string): Expression {
const text = `const _ = ${expr}`;
const srcFile = createSourceFile("test.ts", text, ScriptTarget.Latest, /*setParentNodes*/ true);
return first(cast(first(srcFile.statements), isVariableStatement).declarationList.declarations).initializer!;
}
function inferParameterType(_fn: FunctionOrConstructor, _param: ParameterDeclaration): TypeNode {
// TODO: Inspect function body for clues (see inferFromUsage.ts)
return anyType();
}
// Descends through all nodes in a function, but not in nested functions.
function forEachOwnNodeOfFunction(fnAst: FunctionOrConstructorNode, cb: (node: Node) => void) {
fnAst.body!.forEachChild(function recur(node) {
cb(node);
if (!isFunctionLike(node)) node.forEachChild(recur);
});
}
function isValidIdentifier(name: string): boolean {
const keyword = stringToToken(name);
return !(keyword && isNonContextualKeyword(keyword)) && isIdentifierText(name, ScriptTarget.ESNext);
}
type Modifiers = ReadonlyArray<Modifier> | undefined;
function addComment<T extends Node>(node: T, comment: string | undefined): T {
if (comment !== undefined) addSyntheticLeadingComment(node, SyntaxKind.SingleLineCommentTrivia, comment);
return node;
}
function anyType(): KeywordTypeNode {
return createKeywordTypeNode(SyntaxKind.AnyKeyword);
}
function createNamespace(modifiers: Modifiers, name: string, statements: ReadonlyArray<Statement>): NamespaceDeclaration {
return createModuleDeclaration(/*decorators*/ undefined, modifiers, createIdentifier(name), createModuleBlock(statements), NodeFlags.Namespace) as NamespaceDeclaration;
}
}

View File

@ -1808,25 +1808,36 @@ namespace ts {
return ts.getEditsForFileRename(getProgram()!, oldFilePath, newFilePath, host, formatting.getFormatContext(formatOptions), preferences, sourceMapper);
}
function applyCodeActionCommand(action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
function applyCodeActionCommand(action: CodeActionCommand[]): Promise<ApplyCodeActionCommandResult[]>;
function applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
function applyCodeActionCommand(action: CodeActionCommand, formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult>;
function applyCodeActionCommand(action: CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult[]>;
function applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
function applyCodeActionCommand(fileName: Path, action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
function applyCodeActionCommand(fileName: Path, action: CodeActionCommand[]): Promise<ApplyCodeActionCommandResult[]>;
function applyCodeActionCommand(fileName: Path | CodeActionCommand | CodeActionCommand[], actionOrUndefined?: CodeActionCommand | CodeActionCommand[]): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]> {
const action = typeof fileName === "string" ? actionOrUndefined! : fileName as CodeActionCommand[];
return isArray(action) ? Promise.all(action.map(applySingleCodeActionCommand)) : applySingleCodeActionCommand(action);
function applyCodeActionCommand(fileName: Path | CodeActionCommand | CodeActionCommand[], actionOrFormatSettingsOrUndefined?: CodeActionCommand | CodeActionCommand[] | FormatCodeSettings): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]> {
const action = typeof fileName === "string" ? actionOrFormatSettingsOrUndefined as CodeActionCommand | CodeActionCommand[] : fileName as CodeActionCommand[];
const formatSettings = typeof fileName !== "string" ? actionOrFormatSettingsOrUndefined as FormatCodeSettings : undefined;
return isArray(action) ? Promise.all(action.map(a => applySingleCodeActionCommand(a, formatSettings))) : applySingleCodeActionCommand(action, formatSettings);
}
function applySingleCodeActionCommand(action: CodeActionCommand): Promise<ApplyCodeActionCommandResult> {
function applySingleCodeActionCommand(action: CodeActionCommand, formatSettings: FormatCodeSettings | undefined): Promise<ApplyCodeActionCommandResult> {
const getPath = (path: string): Path => toPath(path, currentDirectory, getCanonicalFileName);
switch (action.type) {
case "install package":
return host.installPackage
? host.installPackage({ fileName: toPath(action.file, currentDirectory, getCanonicalFileName), packageName: action.packageName })
? host.installPackage({ fileName: getPath(action.file), packageName: action.packageName })
: Promise.reject("Host does not implement `installPackage`");
case "generate types": {
const { fileToGenerateTypesFor, outputFileName } = action;
if (!host.inspectValue) return Promise.reject("Host does not implement `installPackage`");
const valueInfoPromise = host.inspectValue({ fileNameToRequire: fileToGenerateTypesFor });
return valueInfoPromise.then(valueInfo => {
const fullOut = getPath(outputFileName);
host.writeFile!(fullOut, valueInfoToDeclarationFileText(valueInfo, formatSettings || testFormatSettings)); // TODO: GH#18217
return { successMessage: `Wrote types to '${fullOut}'` };
});
}
default:
return Debug.fail();
// TODO: Debug.assertNever(action); will only work if there is more than one type.
return Debug.assertNever(action);
}
}

View File

@ -211,8 +211,8 @@ namespace ts.textChanges {
export class ChangeTracker {
private readonly changes: Change[] = [];
private readonly newFiles: { readonly oldFile: SourceFile, readonly fileName: string, readonly statements: ReadonlyArray<Statement> }[] = [];
private readonly classesWithNodesInsertedAtStart = createMap<ClassDeclaration>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
private readonly newFiles: { readonly oldFile: SourceFile | undefined, readonly fileName: string, readonly statements: ReadonlyArray<Statement> }[] = [];
private readonly classesWithNodesInsertedAtStart = createMap<{ readonly node: ClassDeclaration | InterfaceDeclaration | ObjectLiteralExpression, readonly sourceFile: SourceFile }>(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node | NodeArray<TypeParameterDeclaration> }[] = [];
public static fromContext(context: TextChangesContext): ChangeTracker {
@ -410,25 +410,33 @@ namespace ts.textChanges {
}
public insertNodeAtClassStart(sourceFile: SourceFile, cls: ClassLikeDeclaration | InterfaceDeclaration, newElement: ClassElement): void {
this.insertNodeAtStartWorker(sourceFile, cls, newElement);
}
public insertNodeAtObjectStart(sourceFile: SourceFile, obj: ObjectLiteralExpression, newElement: ObjectLiteralElementLike): void {
this.insertNodeAtStartWorker(sourceFile, obj, newElement);
}
private insertNodeAtStartWorker(sourceFile: SourceFile, cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, newElement: ClassElement | ObjectLiteralElementLike): void {
const clsStart = cls.getStart(sourceFile);
const indentation = formatting.SmartIndenter.findFirstNonWhitespaceColumn(getLineStartPositionForPosition(clsStart, sourceFile), clsStart, sourceFile, this.formatContext.options)
+ this.formatContext.options.indentSize!;
this.insertNodeAt(sourceFile, cls.members.pos, newElement, { indentation, ...this.getInsertNodeAtClassStartPrefixSuffix(sourceFile, cls) });
this.insertNodeAt(sourceFile, getMembersOrProperties(cls).pos, newElement, { indentation, ...this.getInsertNodeAtStartPrefixSuffix(sourceFile, cls) });
}
private getInsertNodeAtClassStartPrefixSuffix(sourceFile: SourceFile, cls: ClassLikeDeclaration | InterfaceDeclaration): { prefix: string, suffix: string } {
if (cls.members.length === 0) {
if (addToSeen(this.classesWithNodesInsertedAtStart, getNodeId(cls), cls)) {
private getInsertNodeAtStartPrefixSuffix(sourceFile: SourceFile, cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression): { prefix: string, suffix: string } {
const comma = isObjectLiteralExpression(cls) ? "," : "";
if (getMembersOrProperties(cls).length === 0) {
if (addToSeen(this.classesWithNodesInsertedAtStart, getNodeId(cls), { node: cls, sourceFile })) {
// For `class C {\n}`, don't add the trailing "\n"
const shouldSuffix = (positionsAreOnSameLine as any)(...getClassBraceEnds(cls, sourceFile), sourceFile); // TODO: GH#4130 remove 'as any'
return { prefix: this.newLineCharacter, suffix: shouldSuffix ? this.newLineCharacter : "" };
const shouldSuffix = (positionsAreOnSameLine as any)(...getClassOrObjectBraceEnds(cls, sourceFile), sourceFile); // TODO: GH#4130 remove 'as any'
return { prefix: this.newLineCharacter, suffix: comma + (shouldSuffix ? this.newLineCharacter : "") };
}
else {
return { prefix: "", suffix: this.newLineCharacter };
return { prefix: "", suffix: comma + this.newLineCharacter };
}
}
else {
return { prefix: this.newLineCharacter, suffix: "" };
return { prefix: this.newLineCharacter, suffix: comma };
}
}
@ -647,9 +655,8 @@ namespace ts.textChanges {
}
private finishClassesWithNodesInsertedAtStart(): void {
this.classesWithNodesInsertedAtStart.forEach(cls => {
const sourceFile = cls.getSourceFile();
const [openBraceEnd, closeBraceEnd] = getClassBraceEnds(cls, sourceFile);
this.classesWithNodesInsertedAtStart.forEach(({ node, sourceFile }) => {
const [openBraceEnd, closeBraceEnd] = getClassOrObjectBraceEnds(node, sourceFile);
// For `class C { }` remove the whitespace inside the braces.
if (positionsAreOnSameLine(openBraceEnd, closeBraceEnd, sourceFile) && openBraceEnd !== closeBraceEnd - 1) {
this.deleteRange(sourceFile, createRange(openBraceEnd, closeBraceEnd - 1));
@ -698,7 +705,7 @@ namespace ts.textChanges {
return changes;
}
public createNewFile(oldFile: SourceFile, fileName: string, statements: ReadonlyArray<Statement>) {
public createNewFile(oldFile: SourceFile | undefined, fileName: string, statements: ReadonlyArray<Statement>) {
this.newFiles.push({ oldFile, fileName, statements });
}
}
@ -708,12 +715,19 @@ namespace ts.textChanges {
return skipTrivia(sourceFile.text, getAdjustedStartPosition(sourceFile, node, {}, Position.FullStart), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
}
function getClassBraceEnds(cls: ClassLikeDeclaration | InterfaceDeclaration, sourceFile: SourceFile): [number, number] {
function getClassOrObjectBraceEnds(cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression, sourceFile: SourceFile): [number, number] {
return [findChildOfKind(cls, SyntaxKind.OpenBraceToken, sourceFile)!.end, findChildOfKind(cls, SyntaxKind.CloseBraceToken, sourceFile)!.end];
}
function getMembersOrProperties(cls: ClassLikeDeclaration | InterfaceDeclaration | ObjectLiteralExpression): NodeArray<Node> {
return isObjectLiteralExpression(cls) ? cls.properties : cls.members;
}
export type ValidateNonFormattedText = (node: Node, text: string) => void;
export function getNewFileText(statements: ReadonlyArray<Statement>, scriptKind: ScriptKind, newLineCharacter: string, formatContext: formatting.FormatContext): string {
return changesToText.newFileChangesWorker(/*oldFile*/ undefined, scriptKind, statements, newLineCharacter, formatContext);
}
namespace changesToText {
export function getTextChangesFromChanges(changes: ReadonlyArray<Change>, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): FileTextChanges[] {
return group(changes, c => c.sourceFile.path).map(changesInFile => {
@ -732,13 +746,17 @@ namespace ts.textChanges {
});
}
export function newFileChanges(oldFile: SourceFile, fileName: string, statements: ReadonlyArray<Statement>, newLineCharacter: string, formatContext: formatting.FormatContext): FileTextChanges {
export function newFileChanges(oldFile: SourceFile | undefined, fileName: string, statements: ReadonlyArray<Statement>, newLineCharacter: string, formatContext: formatting.FormatContext): FileTextChanges {
const text = newFileChangesWorker(oldFile, getScriptKindFromFileName(fileName), statements, newLineCharacter, formatContext);
return { fileName, textChanges: [createTextChange(createTextSpan(0, 0), text)], isNewFile: true };
}
export function newFileChangesWorker(oldFile: SourceFile | undefined, scriptKind: ScriptKind, statements: ReadonlyArray<Statement>, newLineCharacter: string, formatContext: formatting.FormatContext): string {
// TODO: this emits the file, parses it back, then formats it that -- may be a less roundabout way to do this
const nonFormattedText = statements.map(s => getNonformattedText(s, oldFile, newLineCharacter).text).join(newLineCharacter);
const sourceFile = createSourceFile(fileName, nonFormattedText, ScriptTarget.ESNext, /*setParentNodes*/ true);
const sourceFile = createSourceFile("any file name", nonFormattedText, ScriptTarget.ESNext, /*setParentNodes*/ true, scriptKind);
const changes = formatting.formatDocument(sourceFile, formatContext);
const text = applyChanges(nonFormattedText, changes);
return { fileName, textChanges: [createTextChange(createTextSpan(0, 0), text)], isNewFile: true };
return applyChanges(nonFormattedText, changes);
}
function computeNewText(change: Change, sourceFile: SourceFile, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string {

View File

@ -69,6 +69,7 @@
"codefixes/inferFromUsage.ts",
"codefixes/fixInvalidImportSyntax.ts",
"codefixes/fixStrictClassInitialization.ts",
"codefixes/generateTypes.ts",
"codefixes/requireInTs.ts",
"codefixes/useDefaultImport.ts",
"codefixes/fixAddModuleReferTypeMissingTypeof.ts",
@ -82,6 +83,6 @@
"services.ts",
"breakpoints.ts",
"transform.ts",
"shims.ts"
"shims.ts",
]
}

View File

@ -231,6 +231,8 @@ namespace ts {
isKnownTypesPackageName?(name: string): boolean;
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
/* @internal */ inspectValue?(options: InspectValueOptions): Promise<ValueInfo>;
writeFile?(fileName: string, content: string): void;
}
/* @internal */
@ -331,9 +333,9 @@ namespace ts {
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray<number>, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray<CodeFixAction>;
getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions;
applyCodeActionCommand(action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
applyCodeActionCommand(action: CodeActionCommand[]): Promise<ApplyCodeActionCommandResult[]>;
applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
applyCodeActionCommand(action: CodeActionCommand, formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult>;
applyCodeActionCommand(action: CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult[]>;
applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
/** @deprecated `fileName` will be ignored */
applyCodeActionCommand(fileName: string, action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
/** @deprecated `fileName` will be ignored */
@ -526,12 +528,22 @@ namespace ts {
// Publicly, this type is just `{}`. Internally it is a union of all the actions we use.
// See `commands?: {}[]` in protocol.ts
export type CodeActionCommand = InstallPackageAction;
export type CodeActionCommand = InstallPackageAction | GenerateTypesAction;
export interface InstallPackageAction {
/* @internal */ file: string;
/* @internal */ type: "install package";
/* @internal */ packageName: string;
/* @internal */ readonly type: "install package";
/* @internal */ readonly file: string;
/* @internal */ readonly packageName: string;
}
export interface GenerateTypesAction extends GenerateTypesOptions {
/* @internal */ readonly type: "generate types";
}
export interface GenerateTypesOptions {
readonly file: string; // File that was importing fileToGenerateTypesFor; used for formatting options.
readonly fileToGenerateTypesFor: string;
readonly outputFileName: string;
}
/**
@ -717,6 +729,31 @@ namespace ts {
indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean;
}
/* @internal */
export const testFormatSettings: FormatCodeSettings = {
baseIndentSize: 0,
indentSize: 4,
tabSize: 4,
newLineCharacter: "\n",
convertTabsToSpaces: true,
indentStyle: IndentStyle.Smart,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterConstructor: false,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
insertSpaceAfterTypeAssertion: false,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
insertSpaceBeforeTypeAnnotation: false
};
export interface DefinitionInfo extends DocumentSpan {
kind: ScriptElementKind;
name: string;

View File

@ -292,7 +292,7 @@ interface Array<T> {}`
cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse },
preferences: emptyOptions,
host: notImplementedHost,
formatContext: formatting.getFormatContext(testFormatOptions)
formatContext: formatting.getFormatContext(testFormatSettings)
};
const diagnostics = languageService.getSuggestionDiagnostics(f.path);

View File

@ -64,27 +64,6 @@ namespace ts {
}
export const newLineCharacter = "\n";
export const testFormatOptions: FormatCodeSettings = {
indentSize: 4,
tabSize: 4,
newLineCharacter,
convertTabsToSpaces: true,
indentStyle: IndentStyle.Smart,
insertSpaceAfterConstructor: false,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
insertSpaceBeforeFunctionParenthesis: false,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
export const notImplementedHost: LanguageServiceHost = {
getCompilationSettings: notImplemented,
@ -123,7 +102,7 @@ namespace ts {
startPosition: selectionRange.pos,
endPosition: selectionRange.end,
host: notImplementedHost,
formatContext: formatting.getFormatContext(testFormatOptions),
formatContext: formatting.getFormatContext(testFormatSettings),
preferences: emptyOptions,
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange));
@ -185,7 +164,7 @@ namespace ts {
startPosition: selectionRange.pos,
endPosition: selectionRange.end,
host: notImplementedHost,
formatContext: formatting.getFormatContext(testFormatOptions),
formatContext: formatting.getFormatContext(testFormatSettings),
preferences: emptyOptions,
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange));

View File

@ -270,7 +270,7 @@ export const Other = 1;
content: "function F() { }",
};
const languageService = makeLanguageService(testFile);
const changes = languageService.organizeImports({ type: "file", fileName: testFile.path }, testFormatOptions, emptyOptions);
const changes = languageService.organizeImports({ type: "file", fileName: testFile.path }, testFormatSettings, emptyOptions);
assert.isEmpty(changes);
});
@ -741,7 +741,7 @@ export * from "lib";
function runBaseline(baselinePath: string, testFile: TestFSWithWatch.File, ...otherFiles: TestFSWithWatch.File[]) {
const { path: testPath, content: testContent } = testFile;
const languageService = makeLanguageService(testFile, ...otherFiles);
const changes = languageService.organizeImports({ type: "file", fileName: testPath }, testFormatOptions, emptyOptions);
const changes = languageService.organizeImports({ type: "file", fileName: testPath }, testFormatSettings, emptyOptions);
assert.equal(changes.length, 1);
assert.equal(changes[0].fileName, testPath);

View File

@ -20,7 +20,7 @@ namespace ts {
const newLineCharacter = getNewLineCharacter(printerOptions);
function getRuleProvider(placeOpenBraceOnNewLineForFunctions: boolean): formatting.FormatContext {
return formatting.getFormatContext(placeOpenBraceOnNewLineForFunctions ? { ...testFormatOptions, placeOpenBraceOnNewLineForFunctions: true } : testFormatOptions);
return formatting.getFormatContext(placeOpenBraceOnNewLineForFunctions ? { ...testFormatSettings, placeOpenBraceOnNewLineForFunctions: true } : testFormatSettings);
}
// validate that positions that were recovered from the printed text actually match positions that will be created if the same text is parsed.

View File

@ -85,6 +85,7 @@ namespace ts.projectSystem {
isKnownTypesPackageName = notImplemented;
installPackage = notImplemented;
inspectValue = notImplemented;
executePendingCommands() {
const actionsToRun = this.postExecActions;
@ -9092,7 +9093,7 @@ export const x = 10;`
Debug.assert(!!project.resolveModuleNames);
const edits = project.getLanguageService().getEditsForFileRename("/old.ts", "/new.ts", testFormatOptions, emptyOptions);
const edits = project.getLanguageService().getEditsForFileRename("/old.ts", "/new.ts", testFormatSettings, emptyOptions);
assert.deepEqual<ReadonlyArray<FileTextChanges>>(edits, [{
fileName: "/user.ts",
textChanges: [{

View File

@ -231,7 +231,8 @@ namespace ts.server {
// buffer, but we have yet to find a way to retrieve that value.
private static readonly maxActiveRequestCount = 10;
private static readonly requestDelayMillis = 100;
private packageInstalledPromise: { resolve(value: ApplyCodeActionCommandResult): void, reject(reason: any): void } | undefined;
private packageInstalledPromise: { resolve(value: ApplyCodeActionCommandResult): void, reject(reason: unknown): void } | undefined;
private inspectValuePromise: { resolve(value: ValueInfo): void } | undefined;
constructor(
private readonly telemetryEnabled: boolean,
@ -261,14 +262,19 @@ namespace ts.server {
}
installPackage(options: InstallPackageOptionsWithProject): Promise<ApplyCodeActionCommandResult> {
const rq: InstallPackageRequest = { kind: "installPackage", ...options };
this.send(rq);
this.send<InstallPackageRequest>({ kind: "installPackage", ...options });
Debug.assert(this.packageInstalledPromise === undefined);
return new Promise((resolve, reject) => {
return new Promise<ApplyCodeActionCommandResult>((resolve, reject) => {
this.packageInstalledPromise = { resolve, reject };
});
}
inspectValue(options: InspectValueOptions): Promise<ValueInfo> {
this.send<InspectValueRequest>({ kind: "inspectValue", options });
Debug.assert(this.inspectValuePromise === undefined);
return new Promise<ValueInfo>(resolve => { this.inspectValuePromise = { resolve }; });
}
attach(projectService: ProjectService) {
this.projectService = projectService;
if (this.logger.hasLevel(LogLevel.requestTime)) {
@ -320,7 +326,7 @@ namespace ts.server {
this.send({ projectName: p.getProjectName(), kind: "closeProject" });
}
private send(rq: TypingInstallerRequestUnion): void {
private send<T extends TypingInstallerRequestUnion>(rq: T): void {
this.installer.send(rq);
}
@ -353,7 +359,7 @@ namespace ts.server {
}
}
private handleMessage(response: TypesRegistryResponse | PackageInstalledResponse | SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse) {
private handleMessage(response: TypesRegistryResponse | PackageInstalledResponse | InspectValueResponse | SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes | InitializationFailedResponse) {
if (this.logger.hasLevel(LogLevel.verbose)) {
this.logger.info(`Received response:${stringifyIndented(response)}`);
}
@ -378,6 +384,10 @@ namespace ts.server {
this.event(response, "setTypings");
break;
}
case ActionValueInspected:
this.inspectValuePromise!.resolve(response.result);
this.inspectValuePromise = undefined;
break;
case EventInitializationFailed:
{
const body: protocol.TypesInstallerInitializationFailedEventBody = {

View File

@ -164,6 +164,11 @@ namespace ts.server.typingsInstaller {
}
break;
}
case "inspectValue": {
const response: InspectValueResponse = { kind: ActionValueInspected, result: inspectModule(req.options.fileNameToRequire) };
this.sendResponse(response);
break;
}
default:
Debug.assertNever(req);
}

View File

@ -4445,6 +4445,7 @@ declare namespace ts.server {
type ActionSet = "action::set";
type ActionInvalidate = "action::invalidate";
type ActionPackageInstalled = "action::packageInstalled";
type ActionValueInspected = "action::valueInspected";
type EventTypesRegistry = "event::typesRegistry";
type EventBeginInstallTypes = "event::beginInstallTypes";
type EventEndInstallTypes = "event::endInstallTypes";
@ -4453,7 +4454,7 @@ declare namespace ts.server {
" __sortedArrayBrand": any;
}
interface TypingInstallerResponse {
readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | ActionPackageInstalled | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed;
readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | ActionPackageInstalled | ActionValueInspected | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed;
}
interface TypingInstallerRequestWithProjectName {
readonly projectName: string;
@ -4661,6 +4662,7 @@ declare namespace ts {
getCustomTransformers?(): CustomTransformers | undefined;
isKnownTypesPackageName?(name: string): boolean;
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
writeFile?(fileName: string, content: string): void;
}
interface LanguageService {
cleanupSemanticCache(): void;
@ -4718,9 +4720,9 @@ declare namespace ts {
toLineColumnOffset?(fileName: string, position: number): LineAndCharacter;
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray<number>, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray<CodeFixAction>;
getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions;
applyCodeActionCommand(action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
applyCodeActionCommand(action: CodeActionCommand[]): Promise<ApplyCodeActionCommandResult[]>;
applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
applyCodeActionCommand(action: CodeActionCommand, formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult>;
applyCodeActionCommand(action: CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult[]>;
applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
/** @deprecated `fileName` will be ignored */
applyCodeActionCommand(fileName: string, action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
/** @deprecated `fileName` will be ignored */
@ -4882,9 +4884,16 @@ declare namespace ts {
changes: ReadonlyArray<FileTextChanges>;
commands?: ReadonlyArray<CodeActionCommand>;
}
type CodeActionCommand = InstallPackageAction;
type CodeActionCommand = InstallPackageAction | GenerateTypesAction;
interface InstallPackageAction {
}
interface GenerateTypesAction extends GenerateTypesOptions {
}
interface GenerateTypesOptions {
readonly file: string;
readonly fileToGenerateTypesFor: string;
readonly outputFileName: string;
}
/**
* A set of one or more available refactoring actions, grouped under a parent refactoring.
*/
@ -8102,6 +8111,7 @@ declare namespace ts.server {
useCaseSensitiveFileNames(): boolean;
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[];
readFile(fileName: string): string | undefined;
writeFile(fileName: string, content: string): void;
fileExists(file: string): boolean;
resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModuleFull[];
getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined;

View File

@ -4445,6 +4445,7 @@ declare namespace ts.server {
type ActionSet = "action::set";
type ActionInvalidate = "action::invalidate";
type ActionPackageInstalled = "action::packageInstalled";
type ActionValueInspected = "action::valueInspected";
type EventTypesRegistry = "event::typesRegistry";
type EventBeginInstallTypes = "event::beginInstallTypes";
type EventEndInstallTypes = "event::endInstallTypes";
@ -4453,7 +4454,7 @@ declare namespace ts.server {
" __sortedArrayBrand": any;
}
interface TypingInstallerResponse {
readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | ActionPackageInstalled | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed;
readonly kind: ActionSet | ActionInvalidate | EventTypesRegistry | ActionPackageInstalled | ActionValueInspected | EventBeginInstallTypes | EventEndInstallTypes | EventInitializationFailed;
}
interface TypingInstallerRequestWithProjectName {
readonly projectName: string;
@ -4661,6 +4662,7 @@ declare namespace ts {
getCustomTransformers?(): CustomTransformers | undefined;
isKnownTypesPackageName?(name: string): boolean;
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
writeFile?(fileName: string, content: string): void;
}
interface LanguageService {
cleanupSemanticCache(): void;
@ -4718,9 +4720,9 @@ declare namespace ts {
toLineColumnOffset?(fileName: string, position: number): LineAndCharacter;
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: ReadonlyArray<number>, formatOptions: FormatCodeSettings, preferences: UserPreferences): ReadonlyArray<CodeFixAction>;
getCombinedCodeFix(scope: CombinedCodeFixScope, fixId: {}, formatOptions: FormatCodeSettings, preferences: UserPreferences): CombinedCodeActions;
applyCodeActionCommand(action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
applyCodeActionCommand(action: CodeActionCommand[]): Promise<ApplyCodeActionCommandResult[]>;
applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[]): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
applyCodeActionCommand(action: CodeActionCommand, formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult>;
applyCodeActionCommand(action: CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult[]>;
applyCodeActionCommand(action: CodeActionCommand | CodeActionCommand[], formatSettings?: FormatCodeSettings): Promise<ApplyCodeActionCommandResult | ApplyCodeActionCommandResult[]>;
/** @deprecated `fileName` will be ignored */
applyCodeActionCommand(fileName: string, action: CodeActionCommand): Promise<ApplyCodeActionCommandResult>;
/** @deprecated `fileName` will be ignored */
@ -4882,9 +4884,16 @@ declare namespace ts {
changes: ReadonlyArray<FileTextChanges>;
commands?: ReadonlyArray<CodeActionCommand>;
}
type CodeActionCommand = InstallPackageAction;
type CodeActionCommand = InstallPackageAction | GenerateTypesAction;
interface InstallPackageAction {
}
interface GenerateTypesAction extends GenerateTypesOptions {
}
interface GenerateTypesOptions {
readonly file: string;
readonly fileToGenerateTypesFor: string;
readonly outputFileName: string;
}
/**
* A set of one or more available refactoring actions, grouped under a parent refactoring.
*/

View File

@ -0,0 +1,236 @@
export class Array {
static from(p0: any): any;
static isArray(p0: any): any;
static of(): any;
concat(p0: any): any;
copyWithin(p0: any, p1: any): any;
entries(): any;
every(p0: any): any;
fill(p0: any): any;
filter(p0: any): any;
find(p0: any): any;
findIndex(p0: any): any;
forEach(p0: any): any;
includes(p0: any): any;
indexOf(p0: any): any;
join(p0: any): any;
keys(): any;
lastIndexOf(p0: any): any;
map(p0: any): any;
pop(): any;
push(p0: any): any;
reduce(p0: any): any;
reduceRight(p0: any): any;
reverse(): any;
shift(): any;
slice(p0: any, p1: any): any;
some(p0: any): any;
sort(p0: any): any;
splice(p0: any, p1: any): any;
toLocaleString(): any;
unshift(p0: any): any;
}
export class Boolean {
constructor(p0: any);
valueOf(): any;
}
export class Date {
static UTC(p0: any, p1: any, p2: any, p3: any, p4: any, p5: any, p6: any): any;
static now(): any;
static parse(p0: any): any;
constructor(p0: any, p1: any, p2: any, p3: any, p4: any, p5: any, p6: any);
getDate(): any;
getDay(): any;
getFullYear(): any;
getHours(): any;
getMilliseconds(): any;
getMinutes(): any;
getMonth(): any;
getSeconds(): any;
getTime(): any;
getTimezoneOffset(): any;
getUTCDate(): any;
getUTCDay(): any;
getUTCFullYear(): any;
getUTCHours(): any;
getUTCMilliseconds(): any;
getUTCMinutes(): any;
getUTCMonth(): any;
getUTCSeconds(): any;
getYear(): any;
setDate(p0: any): any;
setFullYear(p0: any, p1: any, p2: any): any;
setHours(p0: any, p1: any, p2: any, p3: any): any;
setMilliseconds(p0: any): any;
setMinutes(p0: any, p1: any, p2: any): any;
setMonth(p0: any, p1: any): any;
setSeconds(p0: any, p1: any): any;
setTime(p0: any): any;
setUTCDate(p0: any): any;
setUTCFullYear(p0: any, p1: any, p2: any): any;
setUTCHours(p0: any, p1: any, p2: any, p3: any): any;
setUTCMilliseconds(p0: any): any;
setUTCMinutes(p0: any, p1: any, p2: any): any;
setUTCMonth(p0: any, p1: any): any;
setUTCSeconds(p0: any, p1: any): any;
setYear(p0: any): any;
toDateString(): any;
toGMTString(): any;
toISOString(): any;
toJSON(p0: any): any;
toLocaleDateString(): any;
toLocaleString(): any;
toLocaleTimeString(): any;
toTimeString(): any;
toUTCString(): any;
valueOf(): any;
}
export namespace Math {
const E: number;
const LN10: number;
const LN2: number;
const LOG10E: number;
const LOG2E: number;
const PI: number;
const SQRT1_2: number;
const SQRT2: number;
function abs(p0: any): any;
function acos(p0: any): any;
function acosh(p0: any): any;
function asin(p0: any): any;
function asinh(p0: any): any;
function atan(p0: any): any;
function atan2(p0: any, p1: any): any;
function atanh(p0: any): any;
function cbrt(p0: any): any;
function ceil(p0: any): any;
function clz32(p0: any): any;
function cos(p0: any): any;
function cosh(p0: any): any;
function exp(p0: any): any;
function expm1(p0: any): any;
function floor(p0: any): any;
function fround(p0: any): any;
function hypot(p0: any, p1: any): any;
function imul(p0: any, p1: any): any;
function log(p0: any): any;
function log10(p0: any): any;
function log1p(p0: any): any;
function log2(p0: any): any;
function max(p0: any, p1: any): any;
function min(p0: any, p1: any): any;
function pow(p0: any, p1: any): any;
function random(): any;
function round(p0: any): any;
function sign(p0: any): any;
function sin(p0: any): any;
function sinh(p0: any): any;
function sqrt(p0: any): any;
function tan(p0: any): any;
function tanh(p0: any): any;
function trunc(p0: any): any;
}
export class Number {
static EPSILON: number;
static MAX_SAFE_INTEGER: number;
static MAX_VALUE: number;
static MIN_SAFE_INTEGER: number;
static MIN_VALUE: number;
static NEGATIVE_INFINITY: number;
static NaN: number;
static POSITIVE_INFINITY: number;
static isFinite(p0: any): any;
static isInteger(p0: any): any;
static isNaN(p0: any): any;
static isSafeInteger(p0: any): any;
static parseFloat(p0: any): any;
static parseInt(p0: any, p1: any): any;
constructor(p0: any);
toExponential(p0: any): any;
toFixed(p0: any): any;
toLocaleString(): any;
toPrecision(p0: any): any;
toString(p0: any): any;
valueOf(): any;
}
export class RegExp {
static $1: any;
static $2: any;
static $3: any;
static $4: any;
static $5: any;
static $6: any;
static $7: any;
static $8: any;
static $9: any;
static $_: any;
static input: any;
static lastMatch: any;
static lastParen: any;
static leftContext: any;
static rightContext: any;
constructor(p0: any, p1: any);
compile(p0: any, p1: any): any;
exec(p0: any): any;
test(p0: any): any;
}
export class String {
static fromCharCode(p0: any): any;
static fromCodePoint(p0: any): any;
static raw(p0: any): any;
anchor(p0: any): any;
big(): any;
blink(): any;
bold(): any;
charAt(p0: any): any;
charCodeAt(p0: any): any;
codePointAt(p0: any): any;
concat(p0: any): any;
endsWith(p0: any): any;
fixed(): any;
fontcolor(p0: any): any;
fontsize(p0: any): any;
includes(p0: any): any;
indexOf(p0: any): any;
italics(): any;
lastIndexOf(p0: any): any;
link(p0: any): any;
localeCompare(p0: any): any;
match(p0: any): any;
normalize(): any;
repeat(p0: any): any;
replace(p0: any, p1: any): any;
search(p0: any): any;
slice(p0: any, p1: any): any;
small(): any;
split(p0: any, p1: any): any;
startsWith(p0: any): any;
strike(): any;
sub(): any;
substr(p0: any, p1: any): any;
substring(p0: any, p1: any): any;
sup(): any;
toLocaleLowerCase(): any;
toLocaleUpperCase(): any;
toLowerCase(): any;
toUpperCase(): any;
trim(): any;
trimLeft(): any;
trimRight(): any;
valueOf(): any;
}
export class Symbol {
static hasInstance: symbol;
static isConcatSpreadable: symbol;
static iterator: symbol;
static keyFor(p0: any): any;
static match: symbol;
static replace: symbol;
static search: symbol;
static species: symbol;
static split: symbol;
static toPrimitive: symbol;
static toStringTag: symbol;
static unscopables: symbol;
valueOf(): any;
}

View File

@ -0,0 +1,668 @@
export = example;
declare class example {
static VERSION: string;
static add(value: any, other: any): any;
static after(n: any, func: any): any;
static ary(func: any, n: any, guard: any): any;
static assign(...args: any[]): any;
static assignIn(...args: any[]): any;
static assignInWith(...args: any[]): any;
static assignWith(...args: any[]): any;
static at(...args: any[]): any;
static attempt(...args: any[]): any;
static before(n: any, func: any): any;
static bindAll(...args: any[]): any;
static camelCase(string: any): any;
static capitalize(string: any): any;
static castArray(...args: any[]): any;
static ceil(number: any, precision: any): any;
static chain(value: any): any;
static chunk(array: any, size: any, guard: any): any;
static clamp(number: any, lower: any, upper: any): any;
static clone(value: any): any;
static cloneDeep(value: any): any;
static cloneDeepWith(value: any, customizer: any): any;
static cloneWith(value: any, customizer: any): any;
static compact(array: any): any;
static concat(...args: any[]): any;
static cond(pairs: any): any;
static conforms(source: any): any;
static conformsTo(object: any, source: any): any;
static constant(value: any): any;
static countBy(collection: any, iteratee: any): any;
static create(prototype: any, properties: any): any;
static debounce(func: any, wait: any, options: any): any;
static deburr(string: any): any;
static defaultTo(value: any, defaultValue: any): any;
static defaults(...args: any[]): any;
static defaultsDeep(...args: any[]): any;
static defer(...args: any[]): any;
static delay(...args: any[]): any;
static difference(...args: any[]): any;
static differenceBy(...args: any[]): any;
static differenceWith(...args: any[]): any;
static divide(value: any, other: any): any;
static drop(array: any, n: any, guard: any): any;
static dropRight(array: any, n: any, guard: any): any;
static dropRightWhile(array: any, predicate: any): any;
static dropWhile(array: any, predicate: any): any;
static each(collection: any, iteratee: any): any;
static eachRight(collection: any, iteratee: any): any;
static endsWith(string: any, target: any, position: any): any;
static entries(object: any): any;
static entriesIn(object: any): any;
static eq(value: any, other: any): any;
static escape(string: any): any;
static escapeRegExp(string: any): any;
static every(collection: any, predicate: any, guard: any): any;
static extend(...args: any[]): any;
static extendWith(...args: any[]): any;
static fill(array: any, value: any, start: any, end: any): any;
static filter(collection: any, predicate: any): any;
static find(collection: any, predicate: any, fromIndex: any): any;
static findIndex(array: any, predicate: any, fromIndex: any): any;
static findKey(object: any, predicate: any): any;
static findLast(collection: any, predicate: any, fromIndex: any): any;
static findLastIndex(array: any, predicate: any, fromIndex: any): any;
static findLastKey(object: any, predicate: any): any;
static first(array: any): any;
static flatMap(collection: any, iteratee: any): any;
static flatMapDeep(collection: any, iteratee: any): any;
static flatMapDepth(collection: any, iteratee: any, depth: any): any;
static flatten(array: any): any;
static flattenDeep(array: any): any;
static flattenDepth(array: any, depth: any): any;
static flip(func: any): any;
static floor(number: any, precision: any): any;
static flow(...args: any[]): any;
static flowRight(...args: any[]): any;
static forEach(collection: any, iteratee: any): any;
static forEachRight(collection: any, iteratee: any): any;
static forIn(object: any, iteratee: any): any;
static forInRight(object: any, iteratee: any): any;
static forOwn(object: any, iteratee: any): any;
static forOwnRight(object: any, iteratee: any): any;
static fromPairs(pairs: any): any;
static functions(object: any): any;
static functionsIn(object: any): any;
static get(object: any, path: any, defaultValue: any): any;
static groupBy(collection: any, iteratee: any): any;
static gt(value: any, other: any): any;
static gte(value: any, other: any): any;
static has(object: any, path: any): any;
static hasIn(object: any, path: any): any;
static head(array: any): any;
static identity(value: any): any;
static inRange(number: any, start: any, end: any): any;
static includes(collection: any, value: any, fromIndex: any, guard: any): any;
static indexOf(array: any, value: any, fromIndex: any): any;
static initial(array: any): any;
static intersection(...args: any[]): any;
static intersectionBy(...args: any[]): any;
static intersectionWith(...args: any[]): any;
static invert(object: any, iteratee: any): any;
static invertBy(object: any, iteratee: any): any;
static invoke(...args: any[]): any;
static invokeMap(...args: any[]): any;
static isArguments(value: any): any;
static isArray(p0: any): any;
static isArrayBuffer(value: any): any;
static isArrayLike(value: any): any;
static isArrayLikeObject(value: any): any;
static isBoolean(value: any): any;
static isBuffer(b: any): any;
static isDate(value: any): any;
static isElement(value: any): any;
static isEmpty(value: any): any;
static isEqual(value: any, other: any): any;
static isEqualWith(value: any, other: any, customizer: any): any;
static isError(value: any): any;
static isFinite(value: any): any;
static isFunction(value: any): any;
static isInteger(value: any): any;
static isLength(value: any): any;
static isMap(value: any): any;
static isMatch(object: any, source: any): any;
static isMatchWith(object: any, source: any, customizer: any): any;
static isNaN(value: any): any;
static isNative(value: any): any;
static isNil(value: any): any;
static isNull(value: any): any;
static isNumber(value: any): any;
static isObject(value: any): any;
static isObjectLike(value: any): any;
static isPlainObject(value: any): any;
static isRegExp(value: any): any;
static isSafeInteger(value: any): any;
static isSet(value: any): any;
static isString(value: any): any;
static isSymbol(value: any): any;
static isTypedArray(value: any): any;
static isUndefined(value: any): any;
static isWeakMap(value: any): any;
static isWeakSet(value: any): any;
static iteratee(func: any): any;
static join(array: any, separator: any): any;
static kebabCase(string: any): any;
static keyBy(collection: any, iteratee: any): any;
static keys(object: any): any;
static keysIn(object: any): any;
static last(array: any): any;
static lastIndexOf(array: any, value: any, fromIndex: any): any;
static lowerCase(string: any): any;
static lowerFirst(string: any): any;
static lt(value: any, other: any): any;
static lte(value: any, other: any): any;
static map(collection: any, iteratee: any): any;
static mapKeys(object: any, iteratee: any): any;
static mapValues(object: any, iteratee: any): any;
static matches(source: any): any;
static matchesProperty(path: any, srcValue: any): any;
static max(array: any): any;
static maxBy(array: any, iteratee: any): any;
static mean(array: any): any;
static meanBy(array: any, iteratee: any): any;
static merge(...args: any[]): any;
static mergeWith(...args: any[]): any;
static method(...args: any[]): any;
static methodOf(...args: any[]): any;
static min(array: any): any;
static minBy(array: any, iteratee: any): any;
static mixin(object: any, source: any, options: any): any;
static multiply(value: any, other: any): any;
static negate(predicate: any): any;
static noConflict(): any;
static noop(): void;
static now(): any;
static nth(array: any, n: any): any;
static nthArg(n: any): any;
static omit(...args: any[]): any;
static omitBy(object: any, predicate: any): any;
static once(func: any): any;
static orderBy(collection: any, iteratees: any, orders: any, guard: any): any;
static over(...args: any[]): any;
static overArgs(...args: any[]): any;
static overEvery(...args: any[]): any;
static overSome(...args: any[]): any;
static pad(string: any, length: any, chars: any): any;
static padEnd(string: any, length: any, chars: any): any;
static padStart(string: any, length: any, chars: any): any;
static parseInt(string: any, radix: any, guard: any): any;
static partition(collection: any, iteratee: any): any;
static pick(...args: any[]): any;
static pickBy(object: any, predicate: any): any;
static property(path: any): any;
static propertyOf(object: any): any;
static pull(...args: any[]): any;
static pullAll(array: any, values: any): any;
static pullAllBy(array: any, values: any, iteratee: any): any;
static pullAllWith(array: any, values: any, comparator: any): any;
static pullAt(...args: any[]): any;
static random(lower: any, upper: any, floating: any): any;
static range(start: any, end: any, step: any): any;
static rangeRight(start: any, end: any, step: any): any;
static rearg(...args: any[]): any;
static reduce(collection: any, iteratee: any, accumulator: any, ...args: any[]): any;
static reduceRight(collection: any, iteratee: any, accumulator: any, ...args: any[]): any;
static reject(collection: any, predicate: any): any;
static remove(array: any, predicate: any): any;
static repeat(string: any, n: any, guard: any): any;
static replace(...args: any[]): any;
static rest(func: any, start: any): any;
static result(object: any, path: any, defaultValue: any): any;
static reverse(array: any): any;
static round(number: any, precision: any): any;
static runInContext(context: any): any;
static sample(collection: any): any;
static sampleSize(collection: any, n: any, guard: any): any;
static set(object: any, path: any, value: any): any;
static setWith(object: any, path: any, value: any, customizer: any): any;
static shuffle(collection: any): any;
static size(collection: any): any;
static slice(array: any, start: any, end: any): any;
static snakeCase(string: any): any;
static some(collection: any, predicate: any, guard: any): any;
static sortBy(...args: any[]): any;
static sortedIndex(array: any, value: any): any;
static sortedIndexBy(array: any, value: any, iteratee: any): any;
static sortedIndexOf(array: any, value: any): any;
static sortedLastIndex(array: any, value: any): any;
static sortedLastIndexBy(array: any, value: any, iteratee: any): any;
static sortedLastIndexOf(array: any, value: any): any;
static sortedUniq(array: any): any;
static sortedUniqBy(array: any, iteratee: any): any;
static split(string: any, separator: any, limit: any): any;
static spread(func: any, start: any): any;
static startCase(string: any): any;
static startsWith(string: any, target: any, position: any): any;
static stubArray(): any;
static stubFalse(): any;
static stubObject(): any;
static stubString(): any;
static stubTrue(): any;
static subtract(value: any, other: any): any;
static sum(array: any): any;
static sumBy(array: any, iteratee: any): any;
static tail(array: any): any;
static take(array: any, n: any, guard: any): any;
static takeRight(array: any, n: any, guard: any): any;
static takeRightWhile(array: any, predicate: any): any;
static takeWhile(array: any, predicate: any): any;
static tap(value: any, interceptor: any): any;
static template(string: any, options: any, guard: any): any;
static templateSettings: {
escape: RegExp;
evaluate: RegExp;
imports: {};
interpolate: RegExp;
variable: string;
};
static throttle(func: any, wait: any, options: any): any;
static thru(value: any, interceptor: any): any;
static times(n: any, iteratee: any): any;
static toArray(value: any): any;
static toFinite(value: any): any;
static toInteger(value: any): any;
static toLength(value: any): any;
static toLower(value: any): any;
static toNumber(value: any): any;
static toPairs(object: any): any;
static toPairsIn(object: any): any;
static toPath(value: any): any;
static toPlainObject(value: any): any;
static toSafeInteger(value: any): any;
static toString(value: any): any;
static toUpper(value: any): any;
static transform(object: any, iteratee: any, accumulator: any): any;
static trim(string: any, chars: any, guard: any): any;
static trimEnd(string: any, chars: any, guard: any): any;
static trimStart(string: any, chars: any, guard: any): any;
static truncate(string: any, options: any): any;
static unary(func: any): any;
static unescape(string: any): any;
static union(...args: any[]): any;
static unionBy(...args: any[]): any;
static unionWith(...args: any[]): any;
static uniq(array: any): any;
static uniqBy(array: any, iteratee: any): any;
static uniqWith(array: any, comparator: any): any;
static uniqueId(prefix: any): any;
static unset(object: any, path: any): any;
static unzip(array: any): any;
static unzipWith(array: any, iteratee: any): any;
static update(object: any, path: any, updater: any): any;
static updateWith(object: any, path: any, updater: any, customizer: any): any;
static upperCase(string: any): any;
static upperFirst(string: any): any;
static values(object: any): any;
static valuesIn(object: any): any;
static without(...args: any[]): any;
static words(string: any, pattern: any, guard: any): any;
static wrap(value: any, wrapper: any): any;
static xor(...args: any[]): any;
static xorBy(...args: any[]): any;
static xorWith(...args: any[]): any;
static zip(...args: any[]): any;
static zipObject(props: any, values: any): any;
static zipObjectDeep(props: any, values: any): any;
static zipWith(...args: any[]): any;
constructor(value: any);
add(...args: any[]): any;
after(...args: any[]): any;
ary(...args: any[]): any;
assign(...args: any[]): any;
assignIn(...args: any[]): any;
assignInWith(...args: any[]): any;
assignWith(...args: any[]): any;
at(...args: any[]): any;
attempt(...args: any[]): any;
before(...args: any[]): any;
bind(...args: any[]): any;
bindAll(...args: any[]): any;
bindKey(...args: any[]): any;
camelCase(...args: any[]): any;
capitalize(...args: any[]): any;
castArray(...args: any[]): any;
ceil(...args: any[]): any;
chain(): any;
chunk(...args: any[]): any;
clamp(...args: any[]): any;
clone(...args: any[]): any;
cloneDeep(...args: any[]): any;
cloneDeepWith(...args: any[]): any;
cloneWith(...args: any[]): any;
commit(): any;
compact(...args: any[]): any;
concat(...args: any[]): any;
cond(...args: any[]): any;
conforms(...args: any[]): any;
conformsTo(...args: any[]): any;
constant(...args: any[]): any;
countBy(...args: any[]): any;
create(...args: any[]): any;
curry(...args: any[]): any;
curryRight(...args: any[]): any;
debounce(...args: any[]): any;
deburr(...args: any[]): any;
defaultTo(...args: any[]): any;
defaults(...args: any[]): any;
defaultsDeep(...args: any[]): any;
defer(...args: any[]): any;
delay(...args: any[]): any;
difference(...args: any[]): any;
differenceBy(...args: any[]): any;
differenceWith(...args: any[]): any;
divide(...args: any[]): any;
drop(...args: any[]): any;
dropRight(...args: any[]): any;
dropRightWhile(...args: any[]): any;
dropWhile(...args: any[]): any;
each(...args: any[]): any;
eachRight(...args: any[]): any;
endsWith(...args: any[]): any;
entries(...args: any[]): any;
entriesIn(...args: any[]): any;
eq(...args: any[]): any;
escape(...args: any[]): any;
escapeRegExp(...args: any[]): any;
every(...args: any[]): any;
extend(...args: any[]): any;
extendWith(...args: any[]): any;
fill(...args: any[]): any;
filter(...args: any[]): any;
find(...args: any[]): any;
findIndex(...args: any[]): any;
findKey(...args: any[]): any;
findLast(...args: any[]): any;
findLastIndex(...args: any[]): any;
findLastKey(...args: any[]): any;
first(...args: any[]): any;
flatMap(...args: any[]): any;
flatMapDeep(...args: any[]): any;
flatMapDepth(...args: any[]): any;
flatten(...args: any[]): any;
flattenDeep(...args: any[]): any;
flattenDepth(...args: any[]): any;
flip(...args: any[]): any;
floor(...args: any[]): any;
flow(...args: any[]): any;
flowRight(...args: any[]): any;
forEach(...args: any[]): any;
forEachRight(...args: any[]): any;
forIn(...args: any[]): any;
forInRight(...args: any[]): any;
forOwn(...args: any[]): any;
forOwnRight(...args: any[]): any;
fromPairs(...args: any[]): any;
functions(...args: any[]): any;
functionsIn(...args: any[]): any;
get(...args: any[]): any;
groupBy(...args: any[]): any;
gt(...args: any[]): any;
gte(...args: any[]): any;
has(...args: any[]): any;
hasIn(...args: any[]): any;
head(...args: any[]): any;
identity(...args: any[]): any;
inRange(...args: any[]): any;
includes(...args: any[]): any;
indexOf(...args: any[]): any;
initial(...args: any[]): any;
intersection(...args: any[]): any;
intersectionBy(...args: any[]): any;
intersectionWith(...args: any[]): any;
invert(...args: any[]): any;
invertBy(...args: any[]): any;
invoke(...args: any[]): any;
invokeMap(...args: any[]): any;
isArguments(...args: any[]): any;
isArray(...args: any[]): any;
isArrayBuffer(...args: any[]): any;
isArrayLike(...args: any[]): any;
isArrayLikeObject(...args: any[]): any;
isBoolean(...args: any[]): any;
isBuffer(...args: any[]): any;
isDate(...args: any[]): any;
isElement(...args: any[]): any;
isEmpty(...args: any[]): any;
isEqual(...args: any[]): any;
isEqualWith(...args: any[]): any;
isError(...args: any[]): any;
isFinite(...args: any[]): any;
isFunction(...args: any[]): any;
isInteger(...args: any[]): any;
isLength(...args: any[]): any;
isMap(...args: any[]): any;
isMatch(...args: any[]): any;
isMatchWith(...args: any[]): any;
isNaN(...args: any[]): any;
isNative(...args: any[]): any;
isNil(...args: any[]): any;
isNull(...args: any[]): any;
isNumber(...args: any[]): any;
isObject(...args: any[]): any;
isObjectLike(...args: any[]): any;
isPlainObject(...args: any[]): any;
isRegExp(...args: any[]): any;
isSafeInteger(...args: any[]): any;
isSet(...args: any[]): any;
isString(...args: any[]): any;
isSymbol(...args: any[]): any;
isTypedArray(...args: any[]): any;
isUndefined(...args: any[]): any;
isWeakMap(...args: any[]): any;
isWeakSet(...args: any[]): any;
iteratee(...args: any[]): any;
join(...args: any[]): any;
kebabCase(...args: any[]): any;
keyBy(...args: any[]): any;
keys(...args: any[]): any;
keysIn(...args: any[]): any;
last(...args: any[]): any;
lastIndexOf(...args: any[]): any;
lowerCase(...args: any[]): any;
lowerFirst(...args: any[]): any;
lt(...args: any[]): any;
lte(...args: any[]): any;
map(...args: any[]): any;
mapKeys(...args: any[]): any;
mapValues(...args: any[]): any;
matches(...args: any[]): any;
matchesProperty(...args: any[]): any;
max(...args: any[]): any;
maxBy(...args: any[]): any;
mean(...args: any[]): any;
meanBy(...args: any[]): any;
memoize(...args: any[]): any;
merge(...args: any[]): any;
mergeWith(...args: any[]): any;
method(...args: any[]): any;
methodOf(...args: any[]): any;
min(...args: any[]): any;
minBy(...args: any[]): any;
mixin(...args: any[]): any;
multiply(...args: any[]): any;
negate(...args: any[]): any;
next(): any;
noConflict(...args: any[]): any;
noop(...args: any[]): any;
now(...args: any[]): any;
nth(...args: any[]): any;
nthArg(...args: any[]): any;
omit(...args: any[]): any;
omitBy(...args: any[]): any;
once(...args: any[]): any;
orderBy(...args: any[]): any;
over(...args: any[]): any;
overArgs(...args: any[]): any;
overEvery(...args: any[]): any;
overSome(...args: any[]): any;
pad(...args: any[]): any;
padEnd(...args: any[]): any;
padStart(...args: any[]): any;
parseInt(...args: any[]): any;
partial(...args: any[]): any;
partialRight(...args: any[]): any;
partition(...args: any[]): any;
pick(...args: any[]): any;
pickBy(...args: any[]): any;
plant(value: any): any;
pop(...args: any[]): any;
property(...args: any[]): any;
propertyOf(...args: any[]): any;
pull(...args: any[]): any;
pullAll(...args: any[]): any;
pullAllBy(...args: any[]): any;
pullAllWith(...args: any[]): any;
pullAt(...args: any[]): any;
push(...args: any[]): any;
random(...args: any[]): any;
range(...args: any[]): any;
rangeRight(...args: any[]): any;
rearg(...args: any[]): any;
reduce(...args: any[]): any;
reduceRight(...args: any[]): any;
reject(...args: any[]): any;
remove(...args: any[]): any;
repeat(...args: any[]): any;
replace(...args: any[]): any;
rest(...args: any[]): any;
result(...args: any[]): any;
reverse(): any;
round(...args: any[]): any;
runInContext(...args: any[]): any;
sample(...args: any[]): any;
sampleSize(...args: any[]): any;
set(...args: any[]): any;
setWith(...args: any[]): any;
shift(...args: any[]): any;
shuffle(...args: any[]): any;
size(...args: any[]): any;
slice(...args: any[]): any;
snakeCase(...args: any[]): any;
some(...args: any[]): any;
sort(...args: any[]): any;
sortBy(...args: any[]): any;
sortedIndex(...args: any[]): any;
sortedIndexBy(...args: any[]): any;
sortedIndexOf(...args: any[]): any;
sortedLastIndex(...args: any[]): any;
sortedLastIndexBy(...args: any[]): any;
sortedLastIndexOf(...args: any[]): any;
sortedUniq(...args: any[]): any;
sortedUniqBy(...args: any[]): any;
splice(...args: any[]): any;
split(...args: any[]): any;
spread(...args: any[]): any;
startCase(...args: any[]): any;
startsWith(...args: any[]): any;
stubArray(...args: any[]): any;
stubFalse(...args: any[]): any;
stubObject(...args: any[]): any;
stubString(...args: any[]): any;
stubTrue(...args: any[]): any;
subtract(...args: any[]): any;
sum(...args: any[]): any;
sumBy(...args: any[]): any;
tail(...args: any[]): any;
take(...args: any[]): any;
takeRight(...args: any[]): any;
takeRightWhile(...args: any[]): any;
takeWhile(...args: any[]): any;
tap(...args: any[]): any;
template(...args: any[]): any;
throttle(...args: any[]): any;
thru(...args: any[]): any;
times(...args: any[]): any;
toArray(...args: any[]): any;
toFinite(...args: any[]): any;
toInteger(...args: any[]): any;
toJSON(): any;
toLength(...args: any[]): any;
toLower(...args: any[]): any;
toNumber(...args: any[]): any;
toPairs(...args: any[]): any;
toPairsIn(...args: any[]): any;
toPath(...args: any[]): any;
toPlainObject(...args: any[]): any;
toSafeInteger(...args: any[]): any;
toUpper(...args: any[]): any;
transform(...args: any[]): any;
trim(...args: any[]): any;
trimEnd(...args: any[]): any;
trimStart(...args: any[]): any;
truncate(...args: any[]): any;
unary(...args: any[]): any;
unescape(...args: any[]): any;
union(...args: any[]): any;
unionBy(...args: any[]): any;
unionWith(...args: any[]): any;
uniq(...args: any[]): any;
uniqBy(...args: any[]): any;
uniqWith(...args: any[]): any;
uniqueId(...args: any[]): any;
unset(...args: any[]): any;
unshift(...args: any[]): any;
unzip(...args: any[]): any;
unzipWith(...args: any[]): any;
update(...args: any[]): any;
updateWith(...args: any[]): any;
upperCase(...args: any[]): any;
upperFirst(...args: any[]): any;
value(): any;
valueOf(): any;
values(...args: any[]): any;
valuesIn(...args: any[]): any;
without(...args: any[]): any;
words(...args: any[]): any;
wrap(...args: any[]): any;
xor(...args: any[]): any;
xorBy(...args: any[]): any;
xorWith(...args: any[]): any;
zip(...args: any[]): any;
zipObject(...args: any[]): any;
zipObjectDeep(...args: any[]): any;
zipWith(...args: any[]): any;
}
declare namespace example {
function bind(...args: any[]): any;
namespace bind {
// Circular reference from example.bind
const placeholder: any;
}
function bindKey(...args: any[]): any;
namespace bindKey {
// Circular reference from example.bindKey
const placeholder: any;
}
function curry(func: any, arity: any, guard: any): any;
namespace curry {
// Circular reference from example.curry
const placeholder: any;
}
function curryRight(func: any, arity: any, guard: any): any;
namespace curryRight {
// Circular reference from example.curryRight
const placeholder: any;
}
function memoize(func: any, resolver: any): any;
namespace memoize {
class Cache {
constructor(entries: any);
clear(): void;
get(key: any): any;
has(key: any): any;
set(key: any, value: any): any;
}
}
function partial(...args: any[]): any;
namespace partial {
// Circular reference from example.partial
const placeholder: any;
}
function partialRight(...args: any[]): any;
namespace partialRight {
// Circular reference from example.partialRight
const placeholder: any;
}
}

View File

@ -21,12 +21,11 @@ test.setTypesRegistry({
goTo.marker();
verify.codeFixAll({
fixId: "fixCannotFindModule",
fixId: "installTypesPackage",
fixAllDescription: "Install all missing types packages",
newFileContent: {}, // no changes
commands: [
{ packageName: "@types/abs", file: "/a.ts", type: "install package" },
{ packageName: "@types/zap", file: "/a.ts", type: "install package" },
],
newFileContent: `import * as abs from "abs";
import * as zap from "zap";` // unchanged
});

View File

@ -0,0 +1,44 @@
/// <reference path='fourslash.ts' />
// @Filename: /dir/node_modules/plus/index.js
////module.exports = function plus(x, y) { return x + y; };
// @Filename: /dir/a.ts
////import plus = require("plus");
////plus;
// @Filename: /dir/tsconfig.json
////{
//// "compilerOptions": {
//// "paths": {
//// "nota": ["valid_pathmapping"],
//// dontCrash: 37,
//// "*": ["abc123"],
//// }
//// }
////}
goTo.file("/dir/a.ts");
verify.codeFix({
description: "Generate types for 'plus'",
newFileContent: {
"/dir/tsconfig.json":
`{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"*": ["types/*"],
"nota": ["valid_pathmapping"],
dontCrash: 37,
"*": ["abc123"],
}
}
}`,
},
commands: [{
type: "generate types",
file: "/dir/a.ts",
fileToGenerateTypesFor: "/dir/node_modules/plus/index.js",
outputFileName: "/dir/types/plus.d.ts",
}],
});

View File

@ -0,0 +1,47 @@
/// <reference path='fourslash.ts' />
// @Filename: /dir/node_modules/plus/index.js
////module.exports = function plus(x, y) { return x + y; };
// @Filename: /dir/node_modules/times/index.js
////module.exports = function times(x, y) { return x * y; };
// @Filename: /dir/a.ts
////import plus = require("plus");
////import times = require("times");
////plus;
// @Filename: /dir/tsconfig.json
////{
//// "compilerOptions": {}
////}
goTo.file("/dir/a.ts");
verify.codeFixAll({
fixAllDescription: "Generate types for all packages without types",
fixId: "generateTypes",
newFileContent: {
"/dir/tsconfig.json":
// TODO: GH#26618
`{
"compilerOptions": {
"baseUrl": ".",
"paths": { "*": ["types/*"] },
}
}`,
},
commands: [
{
type: "generate types",
file: "/dir/a.ts",
fileToGenerateTypesFor: "/dir/node_modules/plus/index.js",
outputFileName: "/dir/types/plus.d.ts",
},
{
type: "generate types",
file: "/dir/a.ts",
fileToGenerateTypesFor: "/dir/node_modules/times/index.js",
outputFileName: "/dir/types/times.d.ts",
},
],
});

View File

@ -0,0 +1,28 @@
/// <reference path='fourslash.ts' />
// @Filename: /dir/node_modules/plus/index.js
////module.exports = function plus(x, y) { return x * y; };
// @Filename: /dir/a.ts
////import plus = require("plus");
////plus;
// @Filename: /dir/tsconfig.json
////{}
goTo.file("/dir/a.ts");
verify.codeFix({
description: "Generate types for 'plus'",
newFileContent: {
"/dir/tsconfig.json":
`{
"compilerOptions": { "baseUrl": ".", "paths": { "*": ["types/*"] } },
}`,
},
commands: [{
type: "generate types",
file: "/dir/a.ts",
fileToGenerateTypesFor: "/dir/node_modules/plus/index.js",
outputFileName: "/dir/types/plus.d.ts",
}],
});

View File

@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
// @Filename: /a.ts
////import plus = require("plus");
////plus;
// @Filename: /tsconfig.json
////{
//// "compilerOptions": {}
////}
goTo.file("/a.ts");
verify.codeFixAvailable([]);

View File

@ -0,0 +1,30 @@
/// <reference path='fourslash.ts' />
// @Filename: /dir/node_modules/plus/index.js
////module.exports = function plus(x, y) { return x * y; };
// @Filename: /dir/a.ts
////import plus = require("plus");
////plus;
// @Filename: /dir/tsconfig.json
////{
//// "compilerOptions": {
//// "baseUrl": "base",
//// "paths": {
//// "*": ["myTypes/*"],
//// },
//// },
////}
goTo.file("/dir/a.ts");
verify.codeFix({
description: "Generate types for 'plus'",
newFileContent: {}, // no change
commands: [{
type: "generate types",
file: "/dir/a.ts",
fileToGenerateTypesFor: "/dir/node_modules/plus/index.js",
outputFileName: "/dir/base/myTypes/plus.d.ts",
}],
});

View File

@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
// @Filename: /node_modules/foo/index.d.ts
////module.exports = 0;
// @Filename: /a.ts
////import * as foo from "foo";
verify.not.codeFixAvailable();

View File

@ -28,5 +28,5 @@ verify.codeFix({
foo1(arg0: () => number, arg1: () => string, arg2: () => boolean): any {
throw new Error("Method not implemented.");
}
`
`,
});

View File

@ -13,4 +13,4 @@ format.document();
verify.currentFileContentIs(
`const foo = [
1
];`);
];`);

View File

@ -284,7 +284,7 @@ declare namespace FourSlashInterface {
docCommentTemplateAt(markerName: string | FourSlashInterface.Marker, expectedOffset: number, expectedText: string): void;
noDocCommentTemplateAt(markerName: string | FourSlashInterface.Marker): void;
rangeAfterCodeFix(expectedText: string, includeWhiteSpace?: boolean, errorCode?: number, index?: number): void;
codeFixAll(options: { fixId: string, fixAllDescription: string, newFileContent: string, commands?: {}[] }): void;
codeFixAll(options: { fixId: string, fixAllDescription: string, newFileContent: NewFileContent, commands?: {}[] }): void;
fileAfterApplyingRefactorAtMarker(markerName: string, expectedContent: string, refactorNameToApply: string, actionName: string, formattingOptions?: FormatCodeOptions): void;
rangeIs(expectedText: string, includeWhiteSpace?: boolean): void;
fileAfterApplyingRefactorAtMarker(markerName: string, expectedContent: string, refactorNameToApply: string, formattingOptions?: FormatCodeOptions): void;
@ -348,6 +348,8 @@ declare namespace FourSlashInterface {
readonly preferences?: UserPreferences;
}): void;
noMoveToNewFile(): void;
generateTypes(...options: GenerateTypesOptions[]): void;
}
class edit {
backspace(count?: number): void;
@ -637,6 +639,13 @@ declare namespace FourSlashInterface {
readonly text: string | undefined;
}
interface GenerateTypesOptions {
readonly name?: string;
readonly value: unknown;
readonly output?: string | undefined;
readonly outputBaseline?: string;
}
type ArrayOrSingle<T> = T | ReadonlyArray<T>;
type NewFileContent = string | { readonly [fileName: string]: string };
}

View File

@ -0,0 +1,109 @@
/// <reference path="fourslash.ts" />
////dummy text
verify.generateTypes(
{
value: 0,
output:
`export = example;
declare const example: number;`,
},
{
value: (x, y) => x + y,
output:
`export = example;
declare function example(x: any, y: any): void;`,
},
{
// non-arrow functions have different toString(), so important to test
value: function(x, y) {
return x * y;
function inner() {
arguments; // Should not affect type inference
}
},
output:
`export = example;
declare function example(x: any, y: any): any;`,
},
{
value: function(x) { arguments; },
output:
`export = example;
declare function example(x: any, ...args: any[]): void;`,
},
{
value: ({ default() {} }),
output:
`export default function _default(): void;`,
},
{
value: ({ default: class {} }),
output:
`export default class _default {
}`,
},
{
value: new Date(),
output:
`export = example;
declare const example: Date;`,
},
{
value: [0],
output:
`export = example;
declare const example: number[];`,
},
{
value: [() => 0, () => ""],
output:
`export = example;
declare const example: Function[];`,
},
{
value: (() => {
const a = [];
a.push(a);
return a;
})(),
output:
`export = example;
declare const example: any[];`,
},
{
value: (() => {
const o = {
default: 0,
a: 0,
b: "",
self: null,
fn: x => x,
ns1: { x: 0, default: 0 },
ns2: { fn: x => x, default: 0 },
};
o.self = o;
return o;
})(),
output:
`export const a: number;
export const b: string;
export default _default;
export const _default: number;
export function fn(x: any): void;
export const ns1: {
default: number;
x: number;
};
export namespace ns2 {
function fn(x: any): void;
}
// Circular reference from example
export const self: any;`,
},
);

View File

@ -0,0 +1,35 @@
/// <reference path="fourslash.ts" />
////dummy text
verify.generateTypes(
// would like to test against the real "global" but that may vary between node versions.
{
value: {
Array: ignore(Array, ["values"]),
Boolean,
Date,
Math,
Number,
RegExp,
String: ignore(String, ["padStart", "padEnd", "trimStart", "trimEnd"]),
Symbol: ignore(Symbol, ["asyncIterator"]),
},
outputBaseline: "global",
},
{ value: require("lodash"), outputBaseline: "lodash" },
);
// Ignore properties only present in newer versions of node.
function ignore(Cls: Function, ignored: ReadonlyArray<string>): Function {
class Copy {}
for (const name of Object.getOwnPropertyNames(Cls)) {
if (ignored.includes(name) || name === "arguments" || name === "caller" || name === "name" || name === "length" || name === "prototype") continue;
Copy[name] = Cls[name];
}
for (const name of Object.getOwnPropertyNames(Cls.prototype)) {
if (ignored.includes(name)) continue;
Copy.prototype[name] = Cls.prototype[name];
}
return Copy;
}

View File

@ -0,0 +1,109 @@
/// <reference path="fourslash.ts" />
////dummy text
verify.generateTypes(
{
value: class {},
output:
`export = example;
declare class example {
}`,
},
{
value: class {
constructor(x) {
(this as any).x = x;
// Code inside this function should be ignored
function f(this: any) {
this.y = 0;
}
// Same for this class
class Other { constructor() { (this as any).z = 0; } }
}
},
output:
`export = example;
declare class example {
constructor(x: any);
x: any;
}`,
},
{
value: { x: 0, export: 0 },
output: `export const x: number;`,
},
{
value: (() => {
class Super {
superField = 0; // TODO: climb to prototype.constructor and get instance fields?
superMethod() {}
static superStaticMethod() {}
}
class C extends Super {
constructor() {
super();
(this as any)._privateField = 0;
(this as any).field = 0;
}
_privateMethod() {}
method(_p) {
(this as any).otherField = 0; // TODO: include this in output?
}
static _privateStatic() {}
static staticMethod(_s: any) {}
static staticMethodWithNoNamespaceMembers(_p: any) {}
static _privateStaticField = 0;
static staticField = 0;
static innerClass = class {};
}
(C.prototype as any).prototypeNonFunction = 0; // ignored
(C.staticMethod as any).staticMethodProperty = 0;
(C.staticMethod as any)._staticFieldPrivateMember = 0;
(C.prototype.method as any).methodMember = 0; // ignored
// Non-identifier names should be ignored.
(C as any)["&"] = function() {};
(C.prototype as any)["&"] = function() {};
return C;
})(),
output:
`export = example;
declare class example {
static staticField: number;
static staticMethodWithNoNamespaceMembers(_p: any): void;
static superStaticMethod(): void;
field: any;
method(_p: any): void;
superMethod(): void;
}
declare namespace example {
class innerClass {
}
function staticMethod(_s: any): void;
namespace staticMethod {
const staticMethodProperty: number;
}
}`,
},
{
value: (() => {
function F() { this.x = 0; }
(F as any).staticMethod = function() {}
F.prototype.method = function() { }
return F;
})(),
output:
`export = example;
declare class example {
static staticMethod(): void;
x: any;
method(): void;
}`,
},
);