mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into inferredTypeParameterConstraints
This commit is contained in:
+133
-174
@@ -36,7 +36,7 @@ namespace ts.Completions {
|
||||
): CompletionInfo | undefined {
|
||||
if (isInReferenceComment(sourceFile, position)) {
|
||||
const entries = PathCompletions.getTripleSlashReferenceCompletion(sourceFile, position, compilerOptions, host);
|
||||
return entries && pathCompletionsInfo(entries);
|
||||
return entries && convertPathCompletions(entries);
|
||||
}
|
||||
|
||||
const contextToken = findPrecedingToken(position, sourceFile);
|
||||
@@ -44,7 +44,7 @@ namespace ts.Completions {
|
||||
if (isInString(sourceFile, position, contextToken)) {
|
||||
return !contextToken || !isStringLiteral(contextToken) && !isNoSubstitutionTemplateLiteral(contextToken)
|
||||
? undefined
|
||||
: getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host, log);
|
||||
: convertStringLiteralCompletions(getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host), sourceFile, typeChecker, log);
|
||||
}
|
||||
|
||||
if (contextToken && isBreakOrContinueStatement(contextToken.parent)
|
||||
@@ -73,6 +73,34 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
|
||||
function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, sourceFile: SourceFile, checker: TypeChecker, log: Log): CompletionInfo | undefined {
|
||||
if (completion === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
switch (completion.kind) {
|
||||
case StringLiteralCompletionKind.Paths:
|
||||
return convertPathCompletions(completion.paths);
|
||||
case StringLiteralCompletionKind.Properties: {
|
||||
const entries: CompletionEntry[] = [];
|
||||
getCompletionEntriesFromSymbols(completion.symbols, entries, sourceFile, sourceFile, checker, ScriptTarget.ESNext, log, CompletionKind.String); // Target will not be used, so arbitrary
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
|
||||
}
|
||||
case StringLiteralCompletionKind.Types: {
|
||||
const entries = completion.types.map(type => ({ name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.variableElement, sortText: "0" }));
|
||||
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: true, entries };
|
||||
}
|
||||
default:
|
||||
return Debug.assertNever(completion);
|
||||
}
|
||||
}
|
||||
|
||||
function convertPathCompletions(pathCompletions: ReadonlyArray<PathCompletions.PathCompletion>): CompletionInfo {
|
||||
const isGlobalCompletion = false; // We don't want the editor to offer any other completions, such as snippets, inside a comment.
|
||||
const isNewIdentifierLocation = true; // The user may type in a path that doesn't yet exist, creating a "new identifier" with respect to the collection of identifiers the server is aware of.
|
||||
const entries = pathCompletions.map(({ name, kind, span }) => ({ name, kind, kindModifiers: ScriptElementKindModifier.none, sortText: "0", replacementSpan: span }));
|
||||
return { isGlobalCompletion, isMemberCompletion: false, isNewIdentifierLocation, entries };
|
||||
}
|
||||
|
||||
function jsdocCompletionInfo(entries: CompletionEntry[]): CompletionInfo {
|
||||
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
|
||||
}
|
||||
@@ -294,156 +322,6 @@ namespace ts.Completions {
|
||||
}
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringLiteralLike, position: number, typeChecker: TypeChecker, compilerOptions: CompilerOptions, host: LanguageServiceHost, log: Log): CompletionInfo | undefined {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.LiteralType:
|
||||
switch (node.parent.parent.kind) {
|
||||
case SyntaxKind.TypeReference:
|
||||
// TODO: GH#21168
|
||||
return undefined;
|
||||
case SyntaxKind.IndexedAccessType:
|
||||
// Get all apparent property names
|
||||
// i.e. interface Foo {
|
||||
// foo: string;
|
||||
// bar: string;
|
||||
// }
|
||||
// let x: Foo["/*completion position*/"]
|
||||
const type = typeChecker.getTypeFromTypeNode((node.parent.parent as IndexedAccessTypeNode).objectType);
|
||||
return getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess(node, sourceFile, type, typeChecker, compilerOptions.target, log);
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
if (node.parent.parent.kind === SyntaxKind.ObjectLiteralExpression &&
|
||||
(<PropertyAssignment>node.parent).name === node) {
|
||||
// Get quoted name of properties of the object literal expression
|
||||
// i.e. interface ConfigFiles {
|
||||
// 'jspm:dev': string
|
||||
// }
|
||||
// let files: ConfigFiles = {
|
||||
// '/*completion position*/'
|
||||
// }
|
||||
//
|
||||
// function foo(c: ConfigFiles) {}
|
||||
// foo({
|
||||
// '/*completion position*/'
|
||||
// });
|
||||
return getStringLiteralCompletionEntriesFromPropertyAssignment(<PropertyAssignment>node.parent, sourceFile, typeChecker, compilerOptions.target, log);
|
||||
}
|
||||
return fromContextualType();
|
||||
|
||||
case SyntaxKind.ElementAccessExpression: {
|
||||
const { expression, argumentExpression } = node.parent as ElementAccessExpression;
|
||||
if (node === argumentExpression) {
|
||||
// Get all names of properties on the expression
|
||||
// i.e. interface A {
|
||||
// 'prop1': string
|
||||
// }
|
||||
// let a: A;
|
||||
// a['/*completion position*/']
|
||||
const type = typeChecker.getTypeAtLocation(expression);
|
||||
return getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess(node, sourceFile, type, typeChecker, compilerOptions.target, log);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
if (!isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false) && !isImportCall(node.parent)) {
|
||||
const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile);
|
||||
// Get string literal completions from specialized signatures of the target
|
||||
// i.e. declare function f(a: 'A');
|
||||
// f("/*completion position*/")
|
||||
return argumentInfo ? getStringLiteralCompletionEntriesFromCallExpression(argumentInfo, typeChecker) : fromContextualType();
|
||||
}
|
||||
// falls through
|
||||
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
case SyntaxKind.ExternalModuleReference:
|
||||
// Get all known external module names or complete a path to a module
|
||||
// i.e. import * as ns from "/*completion position*/";
|
||||
// var y = import("/*completion position*/");
|
||||
// import x = require("/*completion position*/");
|
||||
// var y = require("/*completion position*/");
|
||||
// export * from "/*completion position*/";
|
||||
return pathCompletionsInfo(PathCompletions.getStringLiteralCompletionsFromModuleNames(sourceFile, node, compilerOptions, host, typeChecker));
|
||||
|
||||
default:
|
||||
return fromContextualType();
|
||||
}
|
||||
|
||||
function fromContextualType(): CompletionInfo {
|
||||
// Get completion for string literal from string literal type
|
||||
// i.e. var x: "hi" | "hello" = "/*completion position*/"
|
||||
return getStringLiteralCompletionEntriesFromType(getContextualTypeFromParent(node, typeChecker), typeChecker);
|
||||
}
|
||||
}
|
||||
|
||||
function pathCompletionsInfo(entries: CompletionEntry[]): CompletionInfo {
|
||||
return {
|
||||
// We don't want the editor to offer any other completions, such as snippets, inside a comment.
|
||||
isGlobalCompletion: false,
|
||||
isMemberCompletion: false,
|
||||
// The user may type in a path that doesn't yet exist, creating a "new identifier"
|
||||
// with respect to the collection of identifiers the server is aware of.
|
||||
isNewIdentifierLocation: true,
|
||||
entries,
|
||||
};
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntriesFromPropertyAssignment(element: ObjectLiteralElement, sourceFile: SourceFile, typeChecker: TypeChecker, target: ScriptTarget, log: Log): CompletionInfo | undefined {
|
||||
const type = typeChecker.getContextualType((<ObjectLiteralExpression>element.parent));
|
||||
const entries: CompletionEntry[] = [];
|
||||
if (type) {
|
||||
getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, element, sourceFile, typeChecker, target, log, CompletionKind.String);
|
||||
if (entries.length) {
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo, typeChecker: TypeChecker): CompletionInfo | undefined {
|
||||
const candidates: Signature[] = [];
|
||||
const entries: CompletionEntry[] = [];
|
||||
const uniques = createMap<true>();
|
||||
|
||||
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
|
||||
|
||||
for (const candidate of candidates) {
|
||||
addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker, uniques);
|
||||
}
|
||||
|
||||
if (entries.length) {
|
||||
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: true, entries };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntriesFromElementAccessOrIndexedAccess(stringLiteralNode: StringLiteral | NoSubstitutionTemplateLiteral, sourceFile: SourceFile, type: Type, typeChecker: TypeChecker, target: ScriptTarget, log: Log): CompletionInfo | undefined {
|
||||
const entries: CompletionEntry[] = [];
|
||||
if (type) {
|
||||
getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, stringLiteralNode, sourceFile, typeChecker, target, log, CompletionKind.String);
|
||||
if (entries.length) {
|
||||
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: true, entries };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getStringLiteralCompletionEntriesFromType(type: Type, typeChecker: TypeChecker): CompletionInfo | undefined {
|
||||
if (type) {
|
||||
const entries: CompletionEntry[] = [];
|
||||
addStringLiteralCompletionsFromType(type, entries, typeChecker);
|
||||
if (entries.length) {
|
||||
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getLabelStatementCompletions(node: Node): CompletionEntry[] {
|
||||
const entries: CompletionEntry[] = [];
|
||||
const uniques = createMap<true>();
|
||||
@@ -470,30 +348,111 @@ namespace ts.Completions {
|
||||
return entries;
|
||||
}
|
||||
|
||||
function addStringLiteralCompletionsFromType(type: Type, result: Push<CompletionEntry>, typeChecker: TypeChecker, uniques = createMap<true>()): void {
|
||||
const enum StringLiteralCompletionKind { Paths, Properties, Types }
|
||||
type StringLiteralCompletion =
|
||||
| { readonly kind: StringLiteralCompletionKind.Paths, readonly paths: ReadonlyArray<PathCompletions.PathCompletion> }
|
||||
| { readonly kind: StringLiteralCompletionKind.Properties, readonly symbols: ReadonlyArray<Symbol> }
|
||||
| { readonly kind: StringLiteralCompletionKind.Types, readonly types: ReadonlyArray<StringLiteralType> };
|
||||
function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringLiteralLike, position: number, typeChecker: TypeChecker, compilerOptions: CompilerOptions, host: LanguageServiceHost): StringLiteralCompletion | undefined {
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.LiteralType:
|
||||
switch (node.parent.parent.kind) {
|
||||
case SyntaxKind.TypeReference:
|
||||
// TODO: GH#21168
|
||||
return undefined;
|
||||
case SyntaxKind.IndexedAccessType:
|
||||
// Get all apparent property names
|
||||
// i.e. interface Foo {
|
||||
// foo: string;
|
||||
// bar: string;
|
||||
// }
|
||||
// let x: Foo["/*completion position*/"]
|
||||
return { kind: StringLiteralCompletionKind.Properties, symbols: typeChecker.getTypeFromTypeNode((node.parent.parent as IndexedAccessTypeNode).objectType).getApparentProperties() };
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
if (isObjectLiteralExpression(node.parent.parent) && (<PropertyAssignment>node.parent).name === node) {
|
||||
// Get quoted name of properties of the object literal expression
|
||||
// i.e. interface ConfigFiles {
|
||||
// 'jspm:dev': string
|
||||
// }
|
||||
// let files: ConfigFiles = {
|
||||
// '/*completion position*/'
|
||||
// }
|
||||
//
|
||||
// function foo(c: ConfigFiles) {}
|
||||
// foo({
|
||||
// '/*completion position*/'
|
||||
// });
|
||||
const type = typeChecker.getContextualType(node.parent.parent);
|
||||
return { kind: StringLiteralCompletionKind.Properties, symbols: type && type.getApparentProperties() };
|
||||
}
|
||||
return fromContextualType();
|
||||
|
||||
case SyntaxKind.ElementAccessExpression: {
|
||||
const { expression, argumentExpression } = node.parent as ElementAccessExpression;
|
||||
if (node === argumentExpression) {
|
||||
// Get all names of properties on the expression
|
||||
// i.e. interface A {
|
||||
// 'prop1': string
|
||||
// }
|
||||
// let a: A;
|
||||
// a['/*completion position*/']
|
||||
return { kind: StringLiteralCompletionKind.Properties, symbols: typeChecker.getTypeAtLocation(expression).getApparentProperties() };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
if (!isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false) && !isImportCall(node.parent)) {
|
||||
const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile);
|
||||
// Get string literal completions from specialized signatures of the target
|
||||
// i.e. declare function f(a: 'A');
|
||||
// f("/*completion position*/")
|
||||
if (argumentInfo) {
|
||||
const candidates: Signature[] = [];
|
||||
typeChecker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
|
||||
const uniques = createMap<true>();
|
||||
return { kind: StringLiteralCompletionKind.Types, types: flatMap(candidates, candidate => getStringLiteralTypes(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), typeChecker, uniques)) };
|
||||
}
|
||||
return fromContextualType();
|
||||
}
|
||||
// falls through (is `require("")` or `import("")`)
|
||||
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
case SyntaxKind.ExternalModuleReference:
|
||||
// Get all known external module names or complete a path to a module
|
||||
// i.e. import * as ns from "/*completion position*/";
|
||||
// var y = import("/*completion position*/");
|
||||
// import x = require("/*completion position*/");
|
||||
// var y = require("/*completion position*/");
|
||||
// export * from "/*completion position*/";
|
||||
return { kind: StringLiteralCompletionKind.Paths, paths: PathCompletions.getStringLiteralCompletionsFromModuleNames(sourceFile, node, compilerOptions, host, typeChecker) };
|
||||
|
||||
default:
|
||||
return fromContextualType();
|
||||
}
|
||||
|
||||
function fromContextualType(): StringLiteralCompletion {
|
||||
// Get completion for string literal from string literal type
|
||||
// i.e. var x: "hi" | "hello" = "/*completion position*/"
|
||||
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker), typeChecker) };
|
||||
}
|
||||
}
|
||||
|
||||
function getStringLiteralTypes(type: Type, typeChecker: TypeChecker, uniques = createMap<true>()): ReadonlyArray<StringLiteralType> {
|
||||
if (type && type.flags & TypeFlags.TypeParameter) {
|
||||
type = typeChecker.getBaseConstraintOfType(type);
|
||||
}
|
||||
if (!type) {
|
||||
return;
|
||||
}
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
for (const t of (<UnionType>type).types) {
|
||||
addStringLiteralCompletionsFromType(t, result, typeChecker, uniques);
|
||||
}
|
||||
}
|
||||
else if (type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral)) {
|
||||
const name = (<StringLiteralType>type).value;
|
||||
if (!uniques.has(name)) {
|
||||
uniques.set(name, true);
|
||||
result.push({
|
||||
name,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
kind: ScriptElementKind.variableElement,
|
||||
sortText: "0"
|
||||
});
|
||||
}
|
||||
type = type.getConstraint();
|
||||
}
|
||||
return type && type.flags & TypeFlags.Union
|
||||
? flatMap((<UnionType>type).types, t => getStringLiteralTypes(t, typeChecker, uniques))
|
||||
: type && type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral) && addToSeen(uniques, (type as StringLiteralType).value)
|
||||
? [type as StringLiteralType]
|
||||
: emptyArray;
|
||||
}
|
||||
|
||||
interface SymbolCompletion {
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
/* @internal */
|
||||
namespace ts.Completions.PathCompletions {
|
||||
export function getStringLiteralCompletionsFromModuleNames(sourceFile: SourceFile, node: LiteralExpression, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionEntry[] {
|
||||
export interface NameAndKind {
|
||||
readonly name: string;
|
||||
readonly kind: ScriptElementKind.scriptElement | ScriptElementKind.directory | ScriptElementKind.externalModuleName;
|
||||
}
|
||||
export interface PathCompletion extends NameAndKind {
|
||||
readonly span: TextSpan;
|
||||
}
|
||||
function createPathCompletion(name: string, kind: PathCompletion["kind"], span: TextSpan): PathCompletion {
|
||||
return { name, kind, span };
|
||||
}
|
||||
|
||||
export function getStringLiteralCompletionsFromModuleNames(sourceFile: SourceFile, node: LiteralExpression, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): PathCompletion[] {
|
||||
const literalValue = normalizeSlashes(node.text);
|
||||
|
||||
const scriptPath = node.getSourceFile().path;
|
||||
@@ -43,12 +54,12 @@ namespace ts.Completions.PathCompletions {
|
||||
compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] {
|
||||
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): PathCompletion[] {
|
||||
const basePath = compilerOptions.project || host.getCurrentDirectory();
|
||||
const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames());
|
||||
const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase);
|
||||
|
||||
const result: CompletionEntry[] = [];
|
||||
const result: PathCompletion[] = [];
|
||||
|
||||
for (const baseDirectory of baseDirectories) {
|
||||
getCompletionEntriesForDirectoryFragment(fragment, baseDirectory, extensions, includeExtensions, span, host, exclude, result);
|
||||
@@ -60,7 +71,7 @@ namespace ts.Completions.PathCompletions {
|
||||
/**
|
||||
* Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename.
|
||||
*/
|
||||
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] {
|
||||
function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: PathCompletion[] = []): PathCompletion[] {
|
||||
if (fragment === undefined) {
|
||||
fragment = "";
|
||||
}
|
||||
@@ -109,7 +120,7 @@ namespace ts.Completions.PathCompletions {
|
||||
}
|
||||
|
||||
forEachKey(foundFiles, foundFile => {
|
||||
result.push(createCompletionEntryForModule(foundFile, ScriptElementKind.scriptElement, span));
|
||||
result.push(createPathCompletion(foundFile, ScriptElementKind.scriptElement, span));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,7 +131,7 @@ namespace ts.Completions.PathCompletions {
|
||||
for (const directory of directories) {
|
||||
const directoryName = getBaseFileName(normalizePath(directory));
|
||||
|
||||
result.push(createCompletionEntryForModule(directoryName, ScriptElementKind.directory, span));
|
||||
result.push(createPathCompletion(directoryName, ScriptElementKind.directory, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,10 +146,10 @@ namespace ts.Completions.PathCompletions {
|
||||
* Modules from node_modules (i.e. those listed in package.json)
|
||||
* This includes all files that are found in node_modules/moduleName/ with acceptable file extensions
|
||||
*/
|
||||
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): CompletionEntry[] {
|
||||
function getCompletionEntriesForNonRelativeModules(fragment: string, scriptPath: string, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, typeChecker: TypeChecker): PathCompletion[] {
|
||||
const { baseUrl, paths } = compilerOptions;
|
||||
|
||||
const result: CompletionEntry[] = [];
|
||||
const result: PathCompletion[] = [];
|
||||
|
||||
const fileExtensions = getSupportedExtensions(compilerOptions);
|
||||
if (baseUrl) {
|
||||
@@ -149,10 +160,10 @@ namespace ts.Completions.PathCompletions {
|
||||
for (const path in paths) {
|
||||
const patterns = paths[path];
|
||||
if (paths.hasOwnProperty(path) && patterns) {
|
||||
for (const pathCompletion of getCompletionsForPathMapping(path, patterns, fragment, baseUrl, fileExtensions, host)) {
|
||||
for (const { name, kind } of getCompletionsForPathMapping(path, patterns, fragment, baseUrl, fileExtensions, host)) {
|
||||
// Path mappings may provide a duplicate way to get to something we've already added, so don't add again.
|
||||
if (!result.some(entry => entry.name === pathCompletion)) {
|
||||
result.push(createCompletionEntryForModule(pathCompletion, ScriptElementKind.externalModuleName, span));
|
||||
if (!result.some(entry => entry.name === name)) {
|
||||
result.push(createPathCompletion(name, kind, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,7 +182,7 @@ namespace ts.Completions.PathCompletions {
|
||||
getCompletionEntriesFromTypings(host, compilerOptions, scriptPath, span, result);
|
||||
|
||||
for (const moduleName of enumeratePotentialNonRelativeModules(fragment, scriptPath, compilerOptions, typeChecker, host)) {
|
||||
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
|
||||
result.push(createPathCompletion(moduleName, ScriptElementKind.externalModuleName, span));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -179,22 +190,22 @@ namespace ts.Completions.PathCompletions {
|
||||
|
||||
function getCompletionsForPathMapping(
|
||||
path: string, patterns: ReadonlyArray<string>, fragment: string, baseUrl: string, fileExtensions: ReadonlyArray<string>, host: LanguageServiceHost,
|
||||
): ReadonlyArray<string> {
|
||||
): ReadonlyArray<NameAndKind> {
|
||||
if (!endsWith(path, "*")) {
|
||||
// For a path mapping "foo": ["/x/y/z.ts"], add "foo" itself as a completion.
|
||||
return !stringContains(path, "*") && startsWith(path, fragment) ? [path] : emptyArray;
|
||||
return !stringContains(path, "*") && startsWith(path, fragment) ? [{ name: path, kind: ScriptElementKind.directory }] : emptyArray;
|
||||
}
|
||||
|
||||
const pathPrefix = path.slice(0, path.length - 1);
|
||||
if (!startsWith(fragment, pathPrefix)) {
|
||||
return [pathPrefix];
|
||||
return [{ name: pathPrefix, kind: ScriptElementKind.directory }];
|
||||
}
|
||||
|
||||
const remainingFragment = fragment.slice(pathPrefix.length);
|
||||
return flatMap(patterns, pattern => getModulesForPathsPattern(remainingFragment, baseUrl, pattern, fileExtensions, host));
|
||||
}
|
||||
|
||||
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: ReadonlyArray<string>, host: LanguageServiceHost): string[] | undefined {
|
||||
function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: ReadonlyArray<string>, host: LanguageServiceHost): ReadonlyArray<NameAndKind> | undefined {
|
||||
if (!host.readDirectory) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -225,14 +236,14 @@ namespace ts.Completions.PathCompletions {
|
||||
// doesn't support. For now, this is safer but slower
|
||||
const includeGlob = normalizedSuffix ? "**/*" : "./*";
|
||||
|
||||
const matches = tryReadDirectory(host, baseDirectory, fileExtensions, /*exclude*/ undefined, [includeGlob]);
|
||||
const directories = tryGetDirectories(host, baseDirectory).map(d => combinePaths(baseDirectory, d));
|
||||
const matches = tryReadDirectory(host, baseDirectory, fileExtensions, /*exclude*/ undefined, [includeGlob]).map<NameAndKind>(name => ({ name, kind: ScriptElementKind.scriptElement }));
|
||||
const directories = tryGetDirectories(host, baseDirectory).map(d => combinePaths(baseDirectory, d)).map<NameAndKind>(name => ({ name, kind: ScriptElementKind.directory }));
|
||||
|
||||
// Trim away prefix and suffix
|
||||
return mapDefined(concatenate(matches, directories), match => {
|
||||
const normalizedMatch = normalizePath(match);
|
||||
return mapDefined<NameAndKind, NameAndKind>(concatenate(matches, directories), ({ name, kind }) => {
|
||||
const normalizedMatch = normalizePath(name);
|
||||
const inner = withoutStartAndEnd(normalizedMatch, completePrefix, normalizedSuffix);
|
||||
return inner !== undefined ? removeLeadingDirectorySeparator(removeFileExtension(inner)) : undefined;
|
||||
return inner !== undefined ? { name: removeLeadingDirectorySeparator(removeFileExtension(inner)), kind } : undefined;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -285,7 +296,7 @@ namespace ts.Completions.PathCompletions {
|
||||
return deduplicate(nonRelativeModuleNames, equateStringsCaseSensitive, compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): CompletionEntry[] | undefined {
|
||||
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): PathCompletion[] | undefined {
|
||||
const token = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false);
|
||||
const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos);
|
||||
const range = commentRanges && find(commentRanges, commentRange => position >= commentRange.pos && position <= commentRange.end);
|
||||
@@ -316,7 +327,7 @@ namespace ts.Completions.PathCompletions {
|
||||
}
|
||||
}
|
||||
|
||||
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: CompletionEntry[] = []): CompletionEntry[] {
|
||||
function getCompletionEntriesFromTypings(host: LanguageServiceHost, options: CompilerOptions, scriptPath: string, span: TextSpan, result: PathCompletion[] = []): PathCompletion[] {
|
||||
// Check for typings specified in compiler options
|
||||
const seen = createMap<true>();
|
||||
if (options.types) {
|
||||
@@ -364,7 +375,7 @@ namespace ts.Completions.PathCompletions {
|
||||
|
||||
function pushResult(moduleName: string) {
|
||||
if (!seen.has(moduleName)) {
|
||||
result.push(createCompletionEntryForModule(moduleName, ScriptElementKind.externalModuleName, span));
|
||||
result.push(createPathCompletion(moduleName, ScriptElementKind.externalModuleName, span));
|
||||
seen.set(moduleName, true);
|
||||
}
|
||||
}
|
||||
@@ -433,10 +444,6 @@ namespace ts.Completions.PathCompletions {
|
||||
}
|
||||
}
|
||||
|
||||
function createCompletionEntryForModule(name: string, kind: ScriptElementKind, replacementSpan: TextSpan): CompletionEntry {
|
||||
return { name, kind, kindModifiers: ScriptElementKindModifier.none, sortText: name, replacementSpan };
|
||||
}
|
||||
|
||||
// Replace everything after the last directory seperator that appears
|
||||
function getDirectoryFragmentTextSpan(text: string, textStart: number): TextSpan {
|
||||
const index = text.lastIndexOf(directorySeparator);
|
||||
@@ -483,8 +490,8 @@ namespace ts.Completions.PathCompletions {
|
||||
return tryIOAndConsumeErrors(host, host.getDirectories, directoryName) || [];
|
||||
}
|
||||
|
||||
function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>): string[] | undefined | undefined {
|
||||
return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include);
|
||||
function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>): ReadonlyArray<string> {
|
||||
return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include) || emptyArray;
|
||||
}
|
||||
|
||||
function tryReadFile(host: LanguageServiceHost, path: string): string | undefined {
|
||||
|
||||
@@ -108,4 +108,36 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(72,43): error TS4081: E
|
||||
!!! error TS2304: Cannot find name 'U'.
|
||||
~
|
||||
!!! error TS4081: Exported type alias 'T62' has or is using private name 'U'.
|
||||
|
||||
// Example from #21496
|
||||
|
||||
type JsonifiedObject<T extends object> = { [K in keyof T]: Jsonified<T[K]> };
|
||||
|
||||
type Jsonified<T> =
|
||||
T extends string | number | boolean | null ? T
|
||||
: T extends undefined | Function ? never // undefined and functions are removed
|
||||
: T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date)
|
||||
: T extends object ? JsonifiedObject<T>
|
||||
: "what is this";
|
||||
|
||||
type Example = {
|
||||
str: "literalstring",
|
||||
fn: () => void,
|
||||
date: Date,
|
||||
customClass: MyClass,
|
||||
obj: {
|
||||
prop: "property",
|
||||
clz: MyClass,
|
||||
nested: { attr: Date }
|
||||
},
|
||||
}
|
||||
|
||||
declare class MyClass {
|
||||
toJSON(): "correct";
|
||||
}
|
||||
|
||||
type JsonifiedExample = Jsonified<Example>;
|
||||
declare let ex: JsonifiedExample;
|
||||
const z1: "correct" = ex.customClass;
|
||||
const z2: string = ex.obj.nested.attr;
|
||||
|
||||
@@ -71,6 +71,38 @@ type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number
|
||||
type T60 = infer U; // Error
|
||||
type T61<T> = infer A extends infer B ? infer C : infer D; // Error
|
||||
type T62<T> = U extends (infer U)[] ? U : U; // Error
|
||||
|
||||
// Example from #21496
|
||||
|
||||
type JsonifiedObject<T extends object> = { [K in keyof T]: Jsonified<T[K]> };
|
||||
|
||||
type Jsonified<T> =
|
||||
T extends string | number | boolean | null ? T
|
||||
: T extends undefined | Function ? never // undefined and functions are removed
|
||||
: T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date)
|
||||
: T extends object ? JsonifiedObject<T>
|
||||
: "what is this";
|
||||
|
||||
type Example = {
|
||||
str: "literalstring",
|
||||
fn: () => void,
|
||||
date: Date,
|
||||
customClass: MyClass,
|
||||
obj: {
|
||||
prop: "property",
|
||||
clz: MyClass,
|
||||
nested: { attr: Date }
|
||||
},
|
||||
}
|
||||
|
||||
declare class MyClass {
|
||||
toJSON(): "correct";
|
||||
}
|
||||
|
||||
type JsonifiedExample = Jsonified<Example>;
|
||||
declare let ex: JsonifiedExample;
|
||||
const z1: "correct" = ex.customClass;
|
||||
const z2: string = ex.obj.nested.attr;
|
||||
|
||||
|
||||
//// [inferTypes1.js]
|
||||
@@ -85,3 +117,5 @@ var C = /** @class */ (function () {
|
||||
}
|
||||
return C;
|
||||
}());
|
||||
var z1 = ex.customClass;
|
||||
var z2 = ex.obj.nested.attr;
|
||||
|
||||
@@ -315,3 +315,106 @@ type T62<T> = U extends (infer U)[] ? U : U; // Error
|
||||
>U : Symbol(U, Decl(inferTypes1.ts, 71, 30))
|
||||
>U : Symbol(U, Decl(inferTypes1.ts, 71, 30))
|
||||
|
||||
// Example from #21496
|
||||
|
||||
type JsonifiedObject<T extends object> = { [K in keyof T]: Jsonified<T[K]> };
|
||||
>JsonifiedObject : Symbol(JsonifiedObject, Decl(inferTypes1.ts, 71, 44))
|
||||
>T : Symbol(T, Decl(inferTypes1.ts, 75, 21))
|
||||
>K : Symbol(K, Decl(inferTypes1.ts, 75, 44))
|
||||
>T : Symbol(T, Decl(inferTypes1.ts, 75, 21))
|
||||
>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 75, 77))
|
||||
>T : Symbol(T, Decl(inferTypes1.ts, 75, 21))
|
||||
>K : Symbol(K, Decl(inferTypes1.ts, 75, 44))
|
||||
|
||||
type Jsonified<T> =
|
||||
>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 75, 77))
|
||||
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
|
||||
|
||||
T extends string | number | boolean | null ? T
|
||||
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
|
||||
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
|
||||
|
||||
: T extends undefined | Function ? never // undefined and functions are removed
|
||||
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
|
||||
>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
|
||||
|
||||
: T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date)
|
||||
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
|
||||
>toJSON : Symbol(toJSON, Decl(inferTypes1.ts, 80, 17))
|
||||
>R : Symbol(R, Decl(inferTypes1.ts, 80, 33))
|
||||
>R : Symbol(R, Decl(inferTypes1.ts, 80, 33))
|
||||
|
||||
: T extends object ? JsonifiedObject<T>
|
||||
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
|
||||
>JsonifiedObject : Symbol(JsonifiedObject, Decl(inferTypes1.ts, 71, 44))
|
||||
>T : Symbol(T, Decl(inferTypes1.ts, 77, 15))
|
||||
|
||||
: "what is this";
|
||||
|
||||
type Example = {
|
||||
>Example : Symbol(Example, Decl(inferTypes1.ts, 82, 21))
|
||||
|
||||
str: "literalstring",
|
||||
>str : Symbol(str, Decl(inferTypes1.ts, 84, 16))
|
||||
|
||||
fn: () => void,
|
||||
>fn : Symbol(fn, Decl(inferTypes1.ts, 85, 25))
|
||||
|
||||
date: Date,
|
||||
>date : Symbol(date, Decl(inferTypes1.ts, 86, 19))
|
||||
>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
|
||||
|
||||
customClass: MyClass,
|
||||
>customClass : Symbol(customClass, Decl(inferTypes1.ts, 87, 15))
|
||||
>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 94, 1))
|
||||
|
||||
obj: {
|
||||
>obj : Symbol(obj, Decl(inferTypes1.ts, 88, 25))
|
||||
|
||||
prop: "property",
|
||||
>prop : Symbol(prop, Decl(inferTypes1.ts, 89, 10))
|
||||
|
||||
clz: MyClass,
|
||||
>clz : Symbol(clz, Decl(inferTypes1.ts, 90, 25))
|
||||
>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 94, 1))
|
||||
|
||||
nested: { attr: Date }
|
||||
>nested : Symbol(nested, Decl(inferTypes1.ts, 91, 21))
|
||||
>attr : Symbol(attr, Decl(inferTypes1.ts, 92, 17))
|
||||
>Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
declare class MyClass {
|
||||
>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 94, 1))
|
||||
|
||||
toJSON(): "correct";
|
||||
>toJSON : Symbol(MyClass.toJSON, Decl(inferTypes1.ts, 96, 23))
|
||||
}
|
||||
|
||||
type JsonifiedExample = Jsonified<Example>;
|
||||
>JsonifiedExample : Symbol(JsonifiedExample, Decl(inferTypes1.ts, 98, 1))
|
||||
>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 75, 77))
|
||||
>Example : Symbol(Example, Decl(inferTypes1.ts, 82, 21))
|
||||
|
||||
declare let ex: JsonifiedExample;
|
||||
>ex : Symbol(ex, Decl(inferTypes1.ts, 101, 11))
|
||||
>JsonifiedExample : Symbol(JsonifiedExample, Decl(inferTypes1.ts, 98, 1))
|
||||
|
||||
const z1: "correct" = ex.customClass;
|
||||
>z1 : Symbol(z1, Decl(inferTypes1.ts, 102, 5))
|
||||
>ex.customClass : Symbol(customClass, Decl(inferTypes1.ts, 87, 15))
|
||||
>ex : Symbol(ex, Decl(inferTypes1.ts, 101, 11))
|
||||
>customClass : Symbol(customClass, Decl(inferTypes1.ts, 87, 15))
|
||||
|
||||
const z2: string = ex.obj.nested.attr;
|
||||
>z2 : Symbol(z2, Decl(inferTypes1.ts, 103, 5))
|
||||
>ex.obj.nested.attr : Symbol(attr, Decl(inferTypes1.ts, 92, 17))
|
||||
>ex.obj.nested : Symbol(nested, Decl(inferTypes1.ts, 91, 21))
|
||||
>ex.obj : Symbol(obj, Decl(inferTypes1.ts, 88, 25))
|
||||
>ex : Symbol(ex, Decl(inferTypes1.ts, 101, 11))
|
||||
>obj : Symbol(obj, Decl(inferTypes1.ts, 88, 25))
|
||||
>nested : Symbol(nested, Decl(inferTypes1.ts, 91, 21))
|
||||
>attr : Symbol(attr, Decl(inferTypes1.ts, 92, 17))
|
||||
|
||||
|
||||
@@ -321,3 +321,107 @@ type T62<T> = U extends (infer U)[] ? U : U; // Error
|
||||
>U : U
|
||||
>U : No type information available!
|
||||
|
||||
// Example from #21496
|
||||
|
||||
type JsonifiedObject<T extends object> = { [K in keyof T]: Jsonified<T[K]> };
|
||||
>JsonifiedObject : JsonifiedObject<T>
|
||||
>T : T
|
||||
>K : K
|
||||
>T : T
|
||||
>Jsonified : Jsonified<T>
|
||||
>T : T
|
||||
>K : K
|
||||
|
||||
type Jsonified<T> =
|
||||
>Jsonified : Jsonified<T>
|
||||
>T : T
|
||||
|
||||
T extends string | number | boolean | null ? T
|
||||
>T : T
|
||||
>null : null
|
||||
>T : T
|
||||
|
||||
: T extends undefined | Function ? never // undefined and functions are removed
|
||||
>T : T
|
||||
>Function : Function
|
||||
|
||||
: T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date)
|
||||
>T : T
|
||||
>toJSON : () => R
|
||||
>R : R
|
||||
>R : R
|
||||
|
||||
: T extends object ? JsonifiedObject<T>
|
||||
>T : T
|
||||
>JsonifiedObject : JsonifiedObject<T>
|
||||
>T : T
|
||||
|
||||
: "what is this";
|
||||
|
||||
type Example = {
|
||||
>Example : Example
|
||||
|
||||
str: "literalstring",
|
||||
>str : "literalstring"
|
||||
|
||||
fn: () => void,
|
||||
>fn : () => void
|
||||
|
||||
date: Date,
|
||||
>date : Date
|
||||
>Date : Date
|
||||
|
||||
customClass: MyClass,
|
||||
>customClass : MyClass
|
||||
>MyClass : MyClass
|
||||
|
||||
obj: {
|
||||
>obj : { prop: "property"; clz: MyClass; nested: { attr: Date; }; }
|
||||
|
||||
prop: "property",
|
||||
>prop : "property"
|
||||
|
||||
clz: MyClass,
|
||||
>clz : MyClass
|
||||
>MyClass : MyClass
|
||||
|
||||
nested: { attr: Date }
|
||||
>nested : { attr: Date; }
|
||||
>attr : Date
|
||||
>Date : Date
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
declare class MyClass {
|
||||
>MyClass : MyClass
|
||||
|
||||
toJSON(): "correct";
|
||||
>toJSON : () => "correct"
|
||||
}
|
||||
|
||||
type JsonifiedExample = Jsonified<Example>;
|
||||
>JsonifiedExample : JsonifiedObject<Example>
|
||||
>Jsonified : Jsonified<T>
|
||||
>Example : Example
|
||||
|
||||
declare let ex: JsonifiedExample;
|
||||
>ex : JsonifiedObject<Example>
|
||||
>JsonifiedExample : JsonifiedObject<Example>
|
||||
|
||||
const z1: "correct" = ex.customClass;
|
||||
>z1 : "correct"
|
||||
>ex.customClass : "correct"
|
||||
>ex : JsonifiedObject<Example>
|
||||
>customClass : "correct"
|
||||
|
||||
const z2: string = ex.obj.nested.attr;
|
||||
>z2 : string
|
||||
>ex.obj.nested.attr : string
|
||||
>ex.obj.nested : JsonifiedObject<{ attr: Date; }>
|
||||
>ex.obj : JsonifiedObject<{ prop: "property"; clz: MyClass; nested: { attr: Date; }; }>
|
||||
>ex : JsonifiedObject<Example>
|
||||
>obj : JsonifiedObject<{ prop: "property"; clz: MyClass; nested: { attr: Date; }; }>
|
||||
>nested : JsonifiedObject<{ attr: Date; }>
|
||||
>attr : string
|
||||
|
||||
|
||||
@@ -73,3 +73,35 @@ type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number
|
||||
type T60 = infer U; // Error
|
||||
type T61<T> = infer A extends infer B ? infer C : infer D; // Error
|
||||
type T62<T> = U extends (infer U)[] ? U : U; // Error
|
||||
|
||||
// Example from #21496
|
||||
|
||||
type JsonifiedObject<T extends object> = { [K in keyof T]: Jsonified<T[K]> };
|
||||
|
||||
type Jsonified<T> =
|
||||
T extends string | number | boolean | null ? T
|
||||
: T extends undefined | Function ? never // undefined and functions are removed
|
||||
: T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date)
|
||||
: T extends object ? JsonifiedObject<T>
|
||||
: "what is this";
|
||||
|
||||
type Example = {
|
||||
str: "literalstring",
|
||||
fn: () => void,
|
||||
date: Date,
|
||||
customClass: MyClass,
|
||||
obj: {
|
||||
prop: "property",
|
||||
clz: MyClass,
|
||||
nested: { attr: Date }
|
||||
},
|
||||
}
|
||||
|
||||
declare class MyClass {
|
||||
toJSON(): "correct";
|
||||
}
|
||||
|
||||
type JsonifiedExample = Jsonified<Example>;
|
||||
declare let ex: JsonifiedExample;
|
||||
const z1: "correct" = ex.customClass;
|
||||
const z2: string = ex.obj.nested.attr;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @Filename: /src/b.ts
|
||||
////not read
|
||||
|
||||
// @Filename: /src/dir/x.ts
|
||||
////not read
|
||||
|
||||
// @Filename: /src/a.ts
|
||||
////import {} from "./[|/*0*/|]";
|
||||
////import {} from "./[|/*1*/|]";
|
||||
|
||||
// @Filename: /tsconfig.json
|
||||
////{
|
||||
//// "compilerOptions": {
|
||||
//// "baseUrl": ".",
|
||||
//// "paths": {
|
||||
//// "foo/*": ["src/*"]
|
||||
//// }
|
||||
//// }
|
||||
////}
|
||||
|
||||
goTo.marker("0");
|
||||
verify.completionListContains("dir", undefined, undefined, "directory");
|
||||
verify.completionListContains("b", undefined, undefined, "script");
|
||||
|
||||
goTo.marker("1");
|
||||
verify.completionListContains("dir", undefined, undefined, "directory");
|
||||
verify.completionListContains("b", undefined, undefined, "script");
|
||||
|
||||
Reference in New Issue
Block a user