mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-03-05 08:47:32 -06:00
Merge branch 'master' into weak-type-errors-on-signature-only-types
This commit is contained in:
commit
781da2332d
@ -34,6 +34,7 @@ function walk(ctx: Lint.WalkContext<void>): void {
|
||||
switch (methodName) {
|
||||
case "apply":
|
||||
case "assert":
|
||||
case "assertEqual":
|
||||
case "call":
|
||||
case "equal":
|
||||
case "fail":
|
||||
@ -69,7 +70,7 @@ function walk(ctx: Lint.WalkContext<void>): void {
|
||||
|
||||
const ranges = ts.getTrailingCommentRanges(sourceFile.text, arg.pos) || ts.getLeadingCommentRanges(sourceFile.text, arg.pos);
|
||||
if (ranges === undefined || ranges.length !== 1 || ranges[0].kind !== ts.SyntaxKind.MultiLineCommentTrivia) {
|
||||
ctx.addFailureAtNode(arg, "Tag boolean argument with parameter name");
|
||||
ctx.addFailureAtNode(arg, "Tag argument with parameter name");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
45
scripts/tslint/debugAssertRule.ts
Normal file
45
scripts/tslint/debugAssertRule.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import * as Lint from "tslint/lib";
|
||||
import * as ts from "typescript";
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
return this.applyWithFunction(sourceFile, ctx => walk(ctx));
|
||||
}
|
||||
}
|
||||
|
||||
function walk(ctx: Lint.WalkContext<void>): void {
|
||||
ts.forEachChild(ctx.sourceFile, function recur(node) {
|
||||
if (ts.isCallExpression(node)) {
|
||||
checkCall(node);
|
||||
}
|
||||
ts.forEachChild(node, recur);
|
||||
});
|
||||
|
||||
function checkCall(node: ts.CallExpression) {
|
||||
if (!isDebugAssert(node.expression) || node.arguments.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = node.arguments[1];
|
||||
if (!ts.isStringLiteral(message)) {
|
||||
ctx.addFailureAtNode(message, "Second argument to 'Debug.assert' should be a string literal.");
|
||||
}
|
||||
|
||||
if (node.arguments.length < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message2 = node.arguments[2];
|
||||
if (!ts.isStringLiteral(message2) && !ts.isArrowFunction(message2)) {
|
||||
ctx.addFailureAtNode(message, "Third argument to 'Debug.assert' should be a string literal or arrow function.");
|
||||
}
|
||||
}
|
||||
|
||||
function isDebugAssert(expr: ts.Node): boolean {
|
||||
return ts.isPropertyAccessExpression(expr) && isName(expr.expression, "Debug") && isName(expr.name, "assert");
|
||||
}
|
||||
|
||||
function isName(expr: ts.Node, text: string): boolean {
|
||||
return ts.isIdentifier(expr) && expr.text === text;
|
||||
}
|
||||
}
|
||||
@ -8074,7 +8074,7 @@ namespace ts {
|
||||
|
||||
function cloneTypeMapper(mapper: TypeMapper): TypeMapper {
|
||||
return mapper && isInferenceContext(mapper) ?
|
||||
createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.inferences) :
|
||||
createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.compareTypes, mapper.inferences) :
|
||||
mapper;
|
||||
}
|
||||
|
||||
@ -8515,7 +8515,7 @@ namespace ts {
|
||||
ignoreReturnTypes: boolean,
|
||||
reportErrors: boolean,
|
||||
errorReporter: ErrorReporter,
|
||||
compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary {
|
||||
compareTypes: TypeComparer): Ternary {
|
||||
// TODO (drosen): De-duplicate code between related functions.
|
||||
if (source === target) {
|
||||
return Ternary.True;
|
||||
@ -8525,7 +8525,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (source.typeParameters) {
|
||||
source = instantiateSignatureInContextOf(source, target);
|
||||
source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes);
|
||||
}
|
||||
|
||||
let result = Ternary.True;
|
||||
@ -9673,6 +9673,11 @@ namespace ts {
|
||||
if (sourceInfo) {
|
||||
return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors);
|
||||
}
|
||||
if (isGenericMappedType(source)) {
|
||||
// A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U }
|
||||
// if T is related to U.
|
||||
return kind === IndexKind.String && isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), targetInfo.type, reportErrors);
|
||||
}
|
||||
if (isObjectLiteralType(source)) {
|
||||
let related = Ternary.True;
|
||||
if (kind === IndexKind.String) {
|
||||
@ -10275,13 +10280,14 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function createInferenceContext(signature: Signature, flags: InferenceFlags, baseInferences?: InferenceInfo[]): InferenceContext {
|
||||
function createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext {
|
||||
const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo);
|
||||
const context = mapper as InferenceContext;
|
||||
context.mappedTypes = signature.typeParameters;
|
||||
context.signature = signature;
|
||||
context.inferences = inferences;
|
||||
context.flags = flags;
|
||||
context.compareTypes = compareTypes || compareTypesAssignable;
|
||||
return context;
|
||||
|
||||
function mapper(t: Type): Type {
|
||||
@ -10729,7 +10735,7 @@ namespace ts {
|
||||
const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]);
|
||||
if (constraint) {
|
||||
const instantiatedConstraint = instantiateType(constraint, context);
|
||||
if (!isTypeAssignableTo(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
|
||||
if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
|
||||
inference.inferredType = inferredType = instantiatedConstraint;
|
||||
}
|
||||
}
|
||||
@ -15130,8 +15136,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
|
||||
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper): Signature {
|
||||
const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes);
|
||||
function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper, compareTypes?: TypeComparer): Signature {
|
||||
const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes, compareTypes);
|
||||
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
|
||||
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
|
||||
inferTypes(context.inferences, instantiateType(source, contextualMapper || identityMapper), target);
|
||||
|
||||
@ -1446,14 +1446,10 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If no includes were specified, exclude common package folders and the outDir
|
||||
const specs = includeSpecs ? [] : ["node_modules", "bower_components", "jspm_packages"];
|
||||
|
||||
const outDir = raw["compilerOptions"] && raw["compilerOptions"]["outDir"];
|
||||
if (outDir) {
|
||||
specs.push(outDir);
|
||||
excludeSpecs = [outDir];
|
||||
}
|
||||
excludeSpecs = specs;
|
||||
}
|
||||
|
||||
if (fileNames === undefined && includeSpecs === undefined) {
|
||||
|
||||
@ -11,20 +11,6 @@ namespace ts {
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
/**
|
||||
* Ternary values are defined such that
|
||||
* x & y is False if either x or y is False.
|
||||
* x & y is Maybe if either x or y is Maybe, but neither x or y is False.
|
||||
* x & y is True if both x and y are True.
|
||||
* x | y is False if both x and y are False.
|
||||
* x | y is Maybe if either x or y is Maybe, but neither x or y is True.
|
||||
* x | y is True if either x or y is True.
|
||||
*/
|
||||
export const enum Ternary {
|
||||
False = 0,
|
||||
Maybe = 1,
|
||||
True = -1
|
||||
}
|
||||
|
||||
// More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times.
|
||||
export const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "accent" }) : undefined;
|
||||
@ -1304,12 +1290,12 @@ namespace ts {
|
||||
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): Diagnostic {
|
||||
const end = start + length;
|
||||
|
||||
Debug.assert(start >= 0, "start must be non-negative, is " + start);
|
||||
Debug.assert(length >= 0, "length must be non-negative, is " + length);
|
||||
Debug.assertGreaterThanOrEqual(start, 0);
|
||||
Debug.assertGreaterThanOrEqual(length, 0);
|
||||
|
||||
if (file) {
|
||||
Debug.assert(start <= file.text.length, `start must be within the bounds of the file. ${start} > ${file.text.length}`);
|
||||
Debug.assert(end <= file.text.length, `end must be the bounds of the file. ${end} > ${file.text.length}`);
|
||||
Debug.assertLessThanOrEqual(start, file.text.length);
|
||||
Debug.assertLessThanOrEqual(end, file.text.length);
|
||||
}
|
||||
|
||||
let text = getLocaleSpecificMessage(message);
|
||||
@ -1889,14 +1875,54 @@ namespace ts {
|
||||
const reservedCharacterPattern = /[^\w\s\/]/g;
|
||||
const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question];
|
||||
|
||||
/**
|
||||
* Matches any single directory segment unless it is the last segment and a .min.js file
|
||||
* Breakdown:
|
||||
* [^./] # matches everything up to the first . character (excluding directory seperators)
|
||||
* (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
|
||||
*/
|
||||
const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*";
|
||||
const singleAsteriskRegexFragmentOther = "[^/]*";
|
||||
/* @internal */
|
||||
export const commonPackageFolders: ReadonlyArray<string> = ["node_modules", "bower_components", "jspm_packages"];
|
||||
|
||||
const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`;
|
||||
|
||||
interface WildcardMatcher {
|
||||
singleAsteriskRegexFragment: string;
|
||||
doubleAsteriskRegexFragment: string;
|
||||
replaceWildcardCharacter: (match: string) => string;
|
||||
}
|
||||
|
||||
const filesMatcher: WildcardMatcher = {
|
||||
/**
|
||||
* Matches any single directory segment unless it is the last segment and a .min.js file
|
||||
* Breakdown:
|
||||
* [^./] # matches everything up to the first . character (excluding directory seperators)
|
||||
* (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
|
||||
*/
|
||||
singleAsteriskRegexFragment: "([^./]|(\\.(?!min\\.js$))?)*",
|
||||
/**
|
||||
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
|
||||
* files or directories, does not match subdirectories that start with a . character
|
||||
*/
|
||||
doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`,
|
||||
replaceWildcardCharacter: match => replaceWildcardCharacter(match, filesMatcher.singleAsteriskRegexFragment)
|
||||
};
|
||||
|
||||
const directoriesMatcher: WildcardMatcher = {
|
||||
singleAsteriskRegexFragment: "[^/]*",
|
||||
/**
|
||||
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
|
||||
* files or directories, does not match subdirectories that start with a . character
|
||||
*/
|
||||
doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`,
|
||||
replaceWildcardCharacter: match => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment)
|
||||
};
|
||||
|
||||
const excludeMatcher: WildcardMatcher = {
|
||||
singleAsteriskRegexFragment: "[^/]*",
|
||||
doubleAsteriskRegexFragment: "(/.+?)?",
|
||||
replaceWildcardCharacter: match => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment)
|
||||
};
|
||||
|
||||
const wildcardMatchers = {
|
||||
files: filesMatcher,
|
||||
directories: directoriesMatcher,
|
||||
exclude: excludeMatcher
|
||||
};
|
||||
|
||||
export function getRegularExpressionForWildcard(specs: ReadonlyArray<string>, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined {
|
||||
const patterns = getRegularExpressionsForWildcards(specs, basePath, usage);
|
||||
@ -1915,17 +1941,8 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const replaceWildcardCharacter = usage === "files" ? replaceWildCardCharacterFiles : replaceWildCardCharacterOther;
|
||||
const singleAsteriskRegexFragment = usage === "files" ? singleAsteriskRegexFragmentFiles : singleAsteriskRegexFragmentOther;
|
||||
|
||||
/**
|
||||
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
|
||||
* files or directories, does not match subdirectories that start with a . character
|
||||
*/
|
||||
const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?";
|
||||
|
||||
return flatMap(specs, spec =>
|
||||
spec && getSubPatternFromSpec(spec, basePath, usage, singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter));
|
||||
spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1936,7 +1953,7 @@ namespace ts {
|
||||
return !/[.*?]/.test(lastPathComponent);
|
||||
}
|
||||
|
||||
function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", singleAsteriskRegexFragment: string, doubleAsteriskRegexFragment: string, replaceWildcardCharacter: (match: string) => string): string | undefined {
|
||||
function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined {
|
||||
let subpattern = "";
|
||||
let hasRecursiveDirectoryWildcard = false;
|
||||
let hasWrittenComponent = false;
|
||||
@ -1975,20 +1992,36 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (usage !== "exclude") {
|
||||
let componentPattern = "";
|
||||
// The * and ? wildcards should not match directories or files that start with . if they
|
||||
// appear first in a component. Dotted directories and files can be included explicitly
|
||||
// like so: **/.*/.*
|
||||
if (component.charCodeAt(0) === CharacterCodes.asterisk) {
|
||||
subpattern += "([^./]" + singleAsteriskRegexFragment + ")?";
|
||||
componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?";
|
||||
component = component.substr(1);
|
||||
}
|
||||
else if (component.charCodeAt(0) === CharacterCodes.question) {
|
||||
subpattern += "[^./]";
|
||||
componentPattern += "[^./]";
|
||||
component = component.substr(1);
|
||||
}
|
||||
}
|
||||
|
||||
subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
|
||||
componentPattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
|
||||
|
||||
// Patterns should not include subfolders like node_modules unless they are
|
||||
// explicitly included as part of the path.
|
||||
//
|
||||
// As an optimization, if the component pattern is the same as the component,
|
||||
// then there definitely were no wildcard characters and we do not need to
|
||||
// add the exclusion pattern.
|
||||
if (componentPattern !== component) {
|
||||
subpattern += implicitExcludePathRegexPattern;
|
||||
}
|
||||
|
||||
subpattern += componentPattern;
|
||||
}
|
||||
else {
|
||||
subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
hasWrittenComponent = true;
|
||||
@ -2002,14 +2035,6 @@ namespace ts {
|
||||
return subpattern;
|
||||
}
|
||||
|
||||
function replaceWildCardCharacterFiles(match: string) {
|
||||
return replaceWildcardCharacter(match, singleAsteriskRegexFragmentFiles);
|
||||
}
|
||||
|
||||
function replaceWildCardCharacterOther(match: string) {
|
||||
return replaceWildcardCharacter(match, singleAsteriskRegexFragmentOther);
|
||||
}
|
||||
|
||||
function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: string) {
|
||||
return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match;
|
||||
}
|
||||
@ -2364,15 +2389,40 @@ namespace ts {
|
||||
return currentAssertionLevel >= level;
|
||||
}
|
||||
|
||||
export function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string, stackCrawlMark?: Function): void {
|
||||
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: Function): void {
|
||||
if (!expression) {
|
||||
if (verboseDebugInfo) {
|
||||
message += "\r\nVerbose Debug Information: " + verboseDebugInfo();
|
||||
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
|
||||
}
|
||||
fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string): void {
|
||||
if (a !== b) {
|
||||
const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
|
||||
fail(`Expected ${a} === ${b}. ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertLessThan(a: number, b: number, msg?: string): void {
|
||||
if (a >= b) {
|
||||
fail(`Expected ${a} < ${b}. ${msg || ""}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertLessThanOrEqual(a: number, b: number): void {
|
||||
if (a > b) {
|
||||
fail(`Expected ${a} <= ${b}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertGreaterThanOrEqual(a: number, b: number): void {
|
||||
if (a < b) {
|
||||
fail(`Expected ${a} >= ${b}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function fail(message?: string, stackCrawlMark?: Function): void {
|
||||
debugger;
|
||||
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
|
||||
|
||||
@ -678,7 +678,7 @@ namespace ts {
|
||||
const { resolvedModule, failedLookupLocations } =
|
||||
nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ts.ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true);
|
||||
if (!resolvedModule) {
|
||||
throw new Error(`Could not resolve JS module ${moduleName} starting at ${initialDir}. Looked in: ${failedLookupLocations.join(", ")}`);
|
||||
throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations.join(", ")}`);
|
||||
}
|
||||
return resolvedModule.resolvedFileName;
|
||||
}
|
||||
|
||||
@ -2448,7 +2448,7 @@ namespace ts {
|
||||
* @param location An optional source map location for the statement.
|
||||
*/
|
||||
function createInlineBreak(label: Label, location?: TextRange): ReturnStatement {
|
||||
Debug.assert(label > 0, `Invalid label: ${label}`);
|
||||
Debug.assertLessThan(0, label, "Invalid label");
|
||||
return setTextRange(
|
||||
createReturn(
|
||||
createArrayLiteral([
|
||||
|
||||
@ -3425,11 +3425,29 @@ namespace ts {
|
||||
AnyDefault = 1 << 2, // Infer anyType for no inferences (otherwise emptyObjectType)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ternary values are defined such that
|
||||
* x & y is False if either x or y is False.
|
||||
* x & y is Maybe if either x or y is Maybe, but neither x or y is False.
|
||||
* x & y is True if both x and y are True.
|
||||
* x | y is False if both x and y are False.
|
||||
* x | y is Maybe if either x or y is Maybe, but neither x or y is True.
|
||||
* x | y is True if either x or y is True.
|
||||
*/
|
||||
export const enum Ternary {
|
||||
False = 0,
|
||||
Maybe = 1,
|
||||
True = -1
|
||||
}
|
||||
|
||||
export type TypeComparer = (s: Type, t: Type, reportErrors?: boolean) => Ternary;
|
||||
|
||||
/* @internal */
|
||||
export interface InferenceContext extends TypeMapper {
|
||||
signature: Signature; // Generic signature for which inferences are made
|
||||
inferences: InferenceInfo[]; // Inferences made for each type parameter
|
||||
flags: InferenceFlags; // Inference flags
|
||||
compareTypes: TypeComparer; // Type comparer function
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
@ -420,7 +420,7 @@ namespace Utils {
|
||||
|
||||
const maxHarnessFrames = 1;
|
||||
|
||||
export function filterStack(error: Error, stackTraceLimit: number = Infinity) {
|
||||
export function filterStack(error: Error, stackTraceLimit = Infinity) {
|
||||
const stack = <string>(<any>error).stack;
|
||||
if (stack) {
|
||||
const lines = stack.split(/\r\n?|\n/g);
|
||||
|
||||
@ -795,7 +795,7 @@ namespace Harness.LanguageService {
|
||||
default:
|
||||
return {
|
||||
module: undefined,
|
||||
error: "Could not resolve module"
|
||||
error: new Error("Could not resolve module")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -73,6 +73,7 @@ namespace ts {
|
||||
"c:/dev/a.d.ts",
|
||||
"c:/dev/a.js",
|
||||
"c:/dev/b.ts",
|
||||
"c:/dev/x/a.ts",
|
||||
"c:/dev/node_modules/a.ts",
|
||||
"c:/dev/bower_components/a.ts",
|
||||
"c:/dev/jspm_packages/a.ts"
|
||||
@ -141,7 +142,8 @@ namespace ts {
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/b.ts"
|
||||
"c:/dev/b.ts",
|
||||
"c:/dev/x/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
@ -462,7 +464,6 @@ namespace ts {
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
|
||||
it("same named declarations are excluded", () => {
|
||||
const json = {
|
||||
include: [
|
||||
@ -651,71 +652,127 @@ namespace ts {
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with common package folders and no exclusions", () => {
|
||||
const json = {
|
||||
include: [
|
||||
"**/a.ts"
|
||||
]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/bower_components/a.ts",
|
||||
"c:/dev/jspm_packages/a.ts",
|
||||
"c:/dev/node_modules/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with common package folders and exclusions", () => {
|
||||
const json = {
|
||||
include: [
|
||||
"**/a.ts"
|
||||
],
|
||||
exclude: [
|
||||
"a.ts"
|
||||
]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/bower_components/a.ts",
|
||||
"c:/dev/jspm_packages/a.ts",
|
||||
"c:/dev/node_modules/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("with common package folders and empty exclude", () => {
|
||||
const json = {
|
||||
include: [
|
||||
"**/a.ts"
|
||||
],
|
||||
exclude: <string[]>[]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/bower_components/a.ts",
|
||||
"c:/dev/jspm_packages/a.ts",
|
||||
"c:/dev/node_modules/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
describe("with common package folders", () => {
|
||||
it("and no exclusions", () => {
|
||||
const json = {
|
||||
include: [
|
||||
"**/a.ts"
|
||||
]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/x/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("and exclusions", () => {
|
||||
const json = {
|
||||
include: [
|
||||
"**/?.ts"
|
||||
],
|
||||
exclude: [
|
||||
"a.ts"
|
||||
]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/b.ts",
|
||||
"c:/dev/x/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("and empty exclude", () => {
|
||||
const json = {
|
||||
include: [
|
||||
"**/a.ts"
|
||||
],
|
||||
exclude: <string[]>[]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/x/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("and explicit recursive include", () => {
|
||||
const json = {
|
||||
include: [
|
||||
"**/a.ts",
|
||||
"**/node_modules/a.ts"
|
||||
]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/x/a.ts",
|
||||
"c:/dev/node_modules/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("and wildcard include", () => {
|
||||
const json = {
|
||||
include: [
|
||||
"*/a.ts"
|
||||
]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/x/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
it("and explicit wildcard include", () => {
|
||||
const json = {
|
||||
include: [
|
||||
"*/a.ts",
|
||||
"node_modules/a.ts"
|
||||
]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/x/a.ts",
|
||||
"c:/dev/node_modules/a.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
});
|
||||
});
|
||||
it("exclude .js files when allowJs=false", () => {
|
||||
const json = {
|
||||
@ -1066,6 +1123,7 @@ namespace ts {
|
||||
};
|
||||
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
});
|
||||
|
||||
describe("with trailing recursive directory", () => {
|
||||
it("in includes", () => {
|
||||
const json = {
|
||||
@ -1264,6 +1322,7 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("with files or folders that begin with a .", () => {
|
||||
it("that are not explicitly included", () => {
|
||||
const json = {
|
||||
|
||||
2
src/lib/es2015.symbol.wellknown.d.ts
vendored
2
src/lib/es2015.symbol.wellknown.d.ts
vendored
@ -110,7 +110,7 @@ interface Map<K, V> {
|
||||
readonly [Symbol.toStringTag]: "Map";
|
||||
}
|
||||
|
||||
interface WeakMap<K extends object, V>{
|
||||
interface WeakMap<K extends object, V> {
|
||||
readonly [Symbol.toStringTag]: "WeakMap";
|
||||
}
|
||||
|
||||
|
||||
@ -170,7 +170,8 @@ namespace ts.server {
|
||||
log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`);
|
||||
const result = host.require(resolvedPath, moduleName);
|
||||
if (result.error) {
|
||||
log(`Failed to load module: ${JSON.stringify(result.error)}`);
|
||||
const err = result.error.stack || result.error.message || JSON.stringify(result.error);
|
||||
log(`Failed to load module '${moduleName}': ${err}`);
|
||||
return undefined;
|
||||
}
|
||||
return result.module;
|
||||
@ -362,7 +363,7 @@ namespace ts.server {
|
||||
return map(this.program.getSourceFiles(), sourceFile => {
|
||||
const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.path);
|
||||
if (!scriptInfo) {
|
||||
Debug.assert(false, `scriptInfo for a file '${sourceFile.fileName}' is missing.`);
|
||||
Debug.fail(`scriptInfo for a file '${sourceFile.fileName}' is missing.`);
|
||||
}
|
||||
return scriptInfo;
|
||||
});
|
||||
|
||||
@ -116,8 +116,6 @@ namespace ts.server {
|
||||
birthtime: Date;
|
||||
}
|
||||
|
||||
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} };
|
||||
|
||||
const readline: {
|
||||
createInterface(options: ReadLineOptions): NodeJS.EventEmitter;
|
||||
} = require("readline");
|
||||
@ -762,8 +760,16 @@ namespace ts.server {
|
||||
const typingSafeListLocation = findArgument(Arguments.TypingSafeListLocation);
|
||||
const npmLocation = findArgument(Arguments.NpmLocation);
|
||||
|
||||
const globalPlugins = (findArgument("--globalPlugins") || "").split(",");
|
||||
const pluginProbeLocations = (findArgument("--pluginProbeLocations") || "").split(",");
|
||||
function parseStringArray(argName: string): string[] {
|
||||
const arg = findArgument(argName);
|
||||
if (arg === undefined) {
|
||||
return emptyArray as string[]; // TODO: https://github.com/Microsoft/TypeScript/issues/16312
|
||||
}
|
||||
return arg.split(",").filter(name => name !== "");
|
||||
}
|
||||
|
||||
const globalPlugins = parseStringArray("--globalPlugins");
|
||||
const pluginProbeLocations = parseStringArray("--pluginProbeLocations");
|
||||
const allowLocalPluginLoads = hasArgument("--allowLocalPluginLoads");
|
||||
|
||||
const useSingleInferredProject = hasArgument("--useSingleInferredProject");
|
||||
|
||||
@ -337,7 +337,7 @@ namespace ts.server {
|
||||
case ContextEvent:
|
||||
const { project, fileName } = event.data;
|
||||
this.projectService.logger.info(`got context event, updating diagnostics for ${fileName}`);
|
||||
this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], this.changeSeq, (n) => n === this.changeSeq, 100));
|
||||
this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], 100));
|
||||
break;
|
||||
case ConfigFileDiagEvent:
|
||||
const { triggerFile, configFileName, diagnostics } = event.data;
|
||||
@ -453,22 +453,23 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private updateProjectStructure(seq: number, matchSeq: (seq: number) => boolean, ms = 1500) {
|
||||
private updateProjectStructure() {
|
||||
const ms = 1500;
|
||||
const seq = this.changeSeq;
|
||||
this.host.setTimeout(() => {
|
||||
if (matchSeq(seq)) {
|
||||
if (this.changeSeq === seq) {
|
||||
this.projectService.refreshInferredProjects();
|
||||
}
|
||||
}, ms);
|
||||
}
|
||||
|
||||
private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], seq: number, matchSeq: (seq: number) => boolean, ms = 1500, followMs = 200, requireOpen = true) {
|
||||
if (followMs > ms) {
|
||||
followMs = ms;
|
||||
}
|
||||
private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], ms: number, requireOpen = true) {
|
||||
const seq = this.changeSeq;
|
||||
const followMs = Math.min(ms, 200);
|
||||
|
||||
let index = 0;
|
||||
const checkOne = () => {
|
||||
if (matchSeq(seq)) {
|
||||
if (this.changeSeq === seq) {
|
||||
const checkSpec = checkList[index];
|
||||
index++;
|
||||
if (checkSpec.project.containsFile(checkSpec.fileName, requireOpen)) {
|
||||
@ -483,7 +484,7 @@ namespace ts.server {
|
||||
}
|
||||
};
|
||||
|
||||
if ((checkList.length > index) && (matchSeq(seq))) {
|
||||
if (checkList.length > index && this.changeSeq === seq) {
|
||||
next.delay(ms, checkOne);
|
||||
}
|
||||
}
|
||||
@ -1262,14 +1263,14 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void {
|
||||
const checkList = mapDefined(fileNames, uncheckedFileName => {
|
||||
const checkList = mapDefined<string, PendingErrorCheck>(fileNames, uncheckedFileName => {
|
||||
const fileName = toNormalizedPath(uncheckedFileName);
|
||||
const project = this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true);
|
||||
return project && { fileName, project };
|
||||
});
|
||||
|
||||
if (checkList.length > 0) {
|
||||
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay);
|
||||
this.updateErrorCheck(next, checkList, delay);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1283,7 +1284,7 @@ namespace ts.server {
|
||||
scriptInfo.editContent(start, end, args.insertString);
|
||||
this.changeSeq++;
|
||||
}
|
||||
this.updateProjectStructure(this.changeSeq, n => n === this.changeSeq);
|
||||
this.updateProjectStructure();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1638,7 +1639,7 @@ namespace ts.server {
|
||||
const checkList = fileNamesInProject.map(fileName => ({ fileName, project }));
|
||||
// Project level error analysis runs on background files too, therefore
|
||||
// doesn't require the file to be opened
|
||||
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay, 200, /*requireOpen*/ false);
|
||||
this.updateErrorCheck(next, checkList, delay, /*requireOpen*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ declare namespace ts.server {
|
||||
data: any;
|
||||
}
|
||||
|
||||
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} };
|
||||
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: { stack?: string, message?: string } };
|
||||
export interface ServerHost extends System {
|
||||
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
|
||||
clearTimeout(timeoutId: any): void;
|
||||
|
||||
@ -260,11 +260,11 @@ namespace ts {
|
||||
templateStack.pop();
|
||||
}
|
||||
else {
|
||||
Debug.assert(token === SyntaxKind.TemplateMiddle, "Should have been a template middle. Was " + token);
|
||||
Debug.assertEqual(token, SyntaxKind.TemplateMiddle, "Should have been a template middle.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.assert(lastTemplateStackToken === SyntaxKind.OpenBraceToken, "Should have been an open brace. Was: " + token);
|
||||
Debug.assertEqual(lastTemplateStackToken, SyntaxKind.OpenBraceToken, "Should have been an open brace");
|
||||
templateStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1000,7 +1000,7 @@ namespace ts.Completions {
|
||||
const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
|
||||
if (!typeForObject) return false;
|
||||
// In a binding pattern, get only known properties. Everywhere else we will get all possible properties.
|
||||
typeMembers = typeChecker.getPropertiesOfType(typeForObject);
|
||||
typeMembers = typeChecker.getPropertiesOfType(typeForObject).filter((symbol) => !(getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.NonPublicAccessibilityModifier));
|
||||
existingMembers = (<ObjectBindingPattern>objectLikeContainer).elements;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1258,7 +1258,7 @@ namespace ts {
|
||||
// We do not support the scenario where a host can modify a registered
|
||||
// file's script kind, i.e. in one project some file is treated as ".ts"
|
||||
// and in another as ".js"
|
||||
Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind + ") for file: " + path);
|
||||
Debug.assertEqual(hostFileInformation.scriptKind, oldSourceFile.scriptKind, "Registered script kind should match new script kind.", path);
|
||||
|
||||
return documentRegistry.updateDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind);
|
||||
}
|
||||
|
||||
@ -136,7 +136,9 @@ namespace ts.SignatureHelp {
|
||||
|
||||
const kind = invocation.typeArguments && invocation.typeArguments.pos === list.pos ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments;
|
||||
const argumentCount = getArgumentCount(list);
|
||||
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
|
||||
if (argumentIndex !== 0) {
|
||||
Debug.assertLessThan(argumentIndex, argumentCount);
|
||||
}
|
||||
const argumentsSpan = getApplicableSpanForArguments(list, sourceFile);
|
||||
return { kind, invocation, argumentsSpan, argumentIndex, argumentCount };
|
||||
}
|
||||
@ -270,7 +272,9 @@ namespace ts.SignatureHelp {
|
||||
? 1
|
||||
: (<TemplateExpression>tagExpression.template).templateSpans.length + 1;
|
||||
|
||||
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
|
||||
if (argumentIndex !== 0) {
|
||||
Debug.assertLessThan(argumentIndex, argumentCount);
|
||||
}
|
||||
return {
|
||||
kind: ArgumentListKind.TaggedTemplateArguments,
|
||||
invocation: tagExpression,
|
||||
@ -402,7 +406,9 @@ namespace ts.SignatureHelp {
|
||||
};
|
||||
});
|
||||
|
||||
Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`);
|
||||
if (argumentIndex !== 0) {
|
||||
Debug.assertLessThan(argumentIndex, argumentCount);
|
||||
}
|
||||
|
||||
const selectedItemIndex = candidates.indexOf(resolvedSignature);
|
||||
Debug.assert(selectedItemIndex !== -1); // If candidates is non-empty it should always include bestSignature. We check for an empty candidates before calling this function.
|
||||
|
||||
@ -78,11 +78,11 @@ namespace ts {
|
||||
getSourceFile: (fileName) => fileName === normalizePath(inputFileName) ? sourceFile : undefined,
|
||||
writeFile: (name, text) => {
|
||||
if (fileExtensionIs(name, ".map")) {
|
||||
Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`);
|
||||
Debug.assertEqual(sourceMapText, undefined, "Unexpected multiple source map outputs, file:", name);
|
||||
sourceMapText = text;
|
||||
}
|
||||
else {
|
||||
Debug.assert(outputText === undefined, `Unexpected multiple outputs for the file: '${name}'`);
|
||||
Debug.assertEqual(outputText, undefined, "Unexpected multiple outputs, file:", name);
|
||||
outputText = text;
|
||||
}
|
||||
},
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
tests/cases/compiler/indexSignatureAndMappedType.ts(6,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, T>'.
|
||||
tests/cases/compiler/indexSignatureAndMappedType.ts(15,5): error TS2322: Type 'Record<K, U>' is not assignable to type '{ [key: string]: T; }'.
|
||||
Type 'U' is not assignable to type 'T'.
|
||||
tests/cases/compiler/indexSignatureAndMappedType.ts(16,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, U>'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/indexSignatureAndMappedType.ts (3 errors) ====
|
||||
// A mapped type { [P in K]: X }, where K is a generic type, is related to
|
||||
// { [key: string]: Y } if X is related to Y.
|
||||
|
||||
function f1<T, K extends string>(x: { [key: string]: T }, y: Record<K, T>) {
|
||||
x = y;
|
||||
y = x; // Error
|
||||
~
|
||||
!!! error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, T>'.
|
||||
}
|
||||
|
||||
function f2<T>(x: { [key: string]: T }, y: Record<string, T>) {
|
||||
x = y;
|
||||
y = x;
|
||||
}
|
||||
|
||||
function f3<T, U, K extends string>(x: { [key: string]: T }, y: Record<K, U>) {
|
||||
x = y; // Error
|
||||
~
|
||||
!!! error TS2322: Type 'Record<K, U>' is not assignable to type '{ [key: string]: T; }'.
|
||||
!!! error TS2322: Type 'U' is not assignable to type 'T'.
|
||||
y = x; // Error
|
||||
~
|
||||
!!! error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record<K, U>'.
|
||||
}
|
||||
|
||||
// Repro from #14548
|
||||
|
||||
type Dictionary = {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
interface IBaseEntity {
|
||||
name: string;
|
||||
properties: Dictionary;
|
||||
}
|
||||
|
||||
interface IEntity<T extends string> extends IBaseEntity {
|
||||
properties: Record<T, string>;
|
||||
}
|
||||
|
||||
73
tests/baselines/reference/indexSignatureAndMappedType.js
Normal file
73
tests/baselines/reference/indexSignatureAndMappedType.js
Normal file
@ -0,0 +1,73 @@
|
||||
//// [indexSignatureAndMappedType.ts]
|
||||
// A mapped type { [P in K]: X }, where K is a generic type, is related to
|
||||
// { [key: string]: Y } if X is related to Y.
|
||||
|
||||
function f1<T, K extends string>(x: { [key: string]: T }, y: Record<K, T>) {
|
||||
x = y;
|
||||
y = x; // Error
|
||||
}
|
||||
|
||||
function f2<T>(x: { [key: string]: T }, y: Record<string, T>) {
|
||||
x = y;
|
||||
y = x;
|
||||
}
|
||||
|
||||
function f3<T, U, K extends string>(x: { [key: string]: T }, y: Record<K, U>) {
|
||||
x = y; // Error
|
||||
y = x; // Error
|
||||
}
|
||||
|
||||
// Repro from #14548
|
||||
|
||||
type Dictionary = {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
interface IBaseEntity {
|
||||
name: string;
|
||||
properties: Dictionary;
|
||||
}
|
||||
|
||||
interface IEntity<T extends string> extends IBaseEntity {
|
||||
properties: Record<T, string>;
|
||||
}
|
||||
|
||||
|
||||
//// [indexSignatureAndMappedType.js]
|
||||
"use strict";
|
||||
// A mapped type { [P in K]: X }, where K is a generic type, is related to
|
||||
// { [key: string]: Y } if X is related to Y.
|
||||
function f1(x, y) {
|
||||
x = y;
|
||||
y = x; // Error
|
||||
}
|
||||
function f2(x, y) {
|
||||
x = y;
|
||||
y = x;
|
||||
}
|
||||
function f3(x, y) {
|
||||
x = y; // Error
|
||||
y = x; // Error
|
||||
}
|
||||
|
||||
|
||||
//// [indexSignatureAndMappedType.d.ts]
|
||||
declare function f1<T, K extends string>(x: {
|
||||
[key: string]: T;
|
||||
}, y: Record<K, T>): void;
|
||||
declare function f2<T>(x: {
|
||||
[key: string]: T;
|
||||
}, y: Record<string, T>): void;
|
||||
declare function f3<T, U, K extends string>(x: {
|
||||
[key: string]: T;
|
||||
}, y: Record<K, U>): void;
|
||||
declare type Dictionary = {
|
||||
[key: string]: string;
|
||||
};
|
||||
interface IBaseEntity {
|
||||
name: string;
|
||||
properties: Dictionary;
|
||||
}
|
||||
interface IEntity<T extends string> extends IBaseEntity {
|
||||
properties: Record<T, string>;
|
||||
}
|
||||
@ -9,7 +9,7 @@ maxDepthExceeded/root.ts(4,4): error TS2540: Cannot assign to 'rel' because it i
|
||||
"maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file
|
||||
"outDir": "built"
|
||||
},
|
||||
"include": ["**/*"],
|
||||
"include": ["**/*", "node_modules/**/*"],
|
||||
"exclude": ["node_modules/m2/**/*"]
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ maxDepthExceeded/root.ts(4,4): error TS2540: Cannot assign to 'rel' because it i
|
||||
"maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file
|
||||
"outDir": "built"
|
||||
},
|
||||
"include": ["**/*"],
|
||||
"include": ["**/*", "node_modules/**/*"],
|
||||
"exclude": ["node_modules/m2/**/*"]
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
//// [signatureInstantiationWithRecursiveConstraints.ts]
|
||||
// Repro from #17148
|
||||
|
||||
class Foo {
|
||||
myFunc<T extends Foo>(arg: T) {}
|
||||
}
|
||||
|
||||
class Bar {
|
||||
myFunc<T extends Bar>(arg: T) {}
|
||||
}
|
||||
|
||||
const myVar: Foo = new Bar();
|
||||
|
||||
|
||||
//// [signatureInstantiationWithRecursiveConstraints.js]
|
||||
"use strict";
|
||||
// Repro from #17148
|
||||
var Foo = (function () {
|
||||
function Foo() {
|
||||
}
|
||||
Foo.prototype.myFunc = function (arg) { };
|
||||
return Foo;
|
||||
}());
|
||||
var Bar = (function () {
|
||||
function Bar() {
|
||||
}
|
||||
Bar.prototype.myFunc = function (arg) { };
|
||||
return Bar;
|
||||
}());
|
||||
var myVar = new Bar();
|
||||
@ -0,0 +1,30 @@
|
||||
=== tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts ===
|
||||
// Repro from #17148
|
||||
|
||||
class Foo {
|
||||
>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0))
|
||||
|
||||
myFunc<T extends Foo>(arg: T) {}
|
||||
>myFunc : Symbol(Foo.myFunc, Decl(signatureInstantiationWithRecursiveConstraints.ts, 2, 11))
|
||||
>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 9))
|
||||
>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0))
|
||||
>arg : Symbol(arg, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 24))
|
||||
>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 9))
|
||||
}
|
||||
|
||||
class Bar {
|
||||
>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1))
|
||||
|
||||
myFunc<T extends Bar>(arg: T) {}
|
||||
>myFunc : Symbol(Bar.myFunc, Decl(signatureInstantiationWithRecursiveConstraints.ts, 6, 11))
|
||||
>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 9))
|
||||
>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1))
|
||||
>arg : Symbol(arg, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 24))
|
||||
>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 9))
|
||||
}
|
||||
|
||||
const myVar: Foo = new Bar();
|
||||
>myVar : Symbol(myVar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 10, 5))
|
||||
>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0))
|
||||
>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1))
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
=== tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts ===
|
||||
// Repro from #17148
|
||||
|
||||
class Foo {
|
||||
>Foo : Foo
|
||||
|
||||
myFunc<T extends Foo>(arg: T) {}
|
||||
>myFunc : <T extends Foo>(arg: T) => void
|
||||
>T : T
|
||||
>Foo : Foo
|
||||
>arg : T
|
||||
>T : T
|
||||
}
|
||||
|
||||
class Bar {
|
||||
>Bar : Bar
|
||||
|
||||
myFunc<T extends Bar>(arg: T) {}
|
||||
>myFunc : <T extends Bar>(arg: T) => void
|
||||
>T : T
|
||||
>Bar : Bar
|
||||
>arg : T
|
||||
>T : T
|
||||
}
|
||||
|
||||
const myVar: Foo = new Bar();
|
||||
>myVar : Foo
|
||||
>Foo : Foo
|
||||
>new Bar() : Bar
|
||||
>Bar : typeof Bar
|
||||
|
||||
35
tests/cases/compiler/indexSignatureAndMappedType.ts
Normal file
35
tests/cases/compiler/indexSignatureAndMappedType.ts
Normal file
@ -0,0 +1,35 @@
|
||||
// @strict: true
|
||||
// @declaration: true
|
||||
|
||||
// A mapped type { [P in K]: X }, where K is a generic type, is related to
|
||||
// { [key: string]: Y } if X is related to Y.
|
||||
|
||||
function f1<T, K extends string>(x: { [key: string]: T }, y: Record<K, T>) {
|
||||
x = y;
|
||||
y = x; // Error
|
||||
}
|
||||
|
||||
function f2<T>(x: { [key: string]: T }, y: Record<string, T>) {
|
||||
x = y;
|
||||
y = x;
|
||||
}
|
||||
|
||||
function f3<T, U, K extends string>(x: { [key: string]: T }, y: Record<K, U>) {
|
||||
x = y; // Error
|
||||
y = x; // Error
|
||||
}
|
||||
|
||||
// Repro from #14548
|
||||
|
||||
type Dictionary = {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
interface IBaseEntity {
|
||||
name: string;
|
||||
properties: Dictionary;
|
||||
}
|
||||
|
||||
interface IEntity<T extends string> extends IBaseEntity {
|
||||
properties: Record<T, string>;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
// @strict: true
|
||||
|
||||
// Repro from #17148
|
||||
|
||||
class Foo {
|
||||
myFunc<T extends Foo>(arg: T) {}
|
||||
}
|
||||
|
||||
class Bar {
|
||||
myFunc<T extends Bar>(arg: T) {}
|
||||
}
|
||||
|
||||
const myVar: Foo = new Bar();
|
||||
@ -0,0 +1,9 @@
|
||||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
////const { b/**/ } = new class {
|
||||
//// private ab;
|
||||
//// protected bc;
|
||||
////}
|
||||
|
||||
goTo.marker();
|
||||
verify.completionListIsEmpty();
|
||||
@ -4,6 +4,6 @@
|
||||
"maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file
|
||||
"outDir": "built"
|
||||
},
|
||||
"include": ["**/*"],
|
||||
"include": ["**/*", "node_modules/**/*"],
|
||||
"exclude": ["node_modules/m2/**/*"]
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
"check-space"
|
||||
],
|
||||
"curly":[true, "ignore-same-line"],
|
||||
"debug-assert": true,
|
||||
"indent": [true,
|
||||
"spaces"
|
||||
],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user