diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dd56dc8ff8905..c170e2f7a6430 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -258,17 +258,19 @@ namespace ts { } const enum IntrinsicTypeKind { - Uppercase, + Uppercase = 1, Lowercase, Capitalize, - Uncapitalize + Uncapitalize, + TypeToString } const intrinsicTypeKinds: ReadonlyESMap = new Map(getEntries({ Uppercase: IntrinsicTypeKind.Uppercase, Lowercase: IntrinsicTypeKind.Lowercase, Capitalize: IntrinsicTypeKind.Capitalize, - Uncapitalize: IntrinsicTypeKind.Uncapitalize + Uncapitalize: IntrinsicTypeKind.Uncapitalize, + TypeToString: IntrinsicTypeKind.TypeToString })); function SymbolLinks(this: SymbolLinks) { @@ -1197,6 +1199,83 @@ namespace ts { return diagnostic; } + function checkThrowType(location: Node, type: Type) { + if (isThrowType(type)) errorByThrowType(location, type.value); + } + function errorByThrowType(location: Node, type: Type) { + if (isThrowType(type)) type = type.value; + + let message = ""; + const diagnostic = getDiagnosticFromThrowType(type); + if (diagnostic) { + const [base, ...args] = diagnostic; + message = formatMessage(/*_dummy*/0, base, ...map(args, getTypeNameForErrorDisplay)); + } + else message = getMessageFromThrowType(type); + + const category = getCategoryFromThrowType(type); + appendDiagnosticWithCategory(category, location, Diagnostics.Type_instantiated_results_in_a_throw_type_saying_Colon_0, "\n " + message); + } + function isThrowType(type: Type): type is ThrowType { + return !!(type.flags & TypeFlags.ThrowType); + } + + function getMessageFromThrowType(type: Type): string { + if (isThrowType(type)) type = type.value; + if (type.flags & TypeFlags.StringLiteral) return (type as StringLiteralType).value; + const message = getTypeOfPropertyOfType(type, "message" as __String); + if (message) { + if (message.flags & TypeFlags.StringLiteral) return (message as StringLiteralType).value; + } + return getTypeNameForErrorDisplay(type); + } + // function getTypeFromThrowType(type: Type): Type { + // if (type.flags & TypeFlags.ThrowType) type = (type).value; + // return getTypeOfPropertyOfType(type, <__String>"type") || errorType; + // } + function getDiagnosticFromThrowType(type: Type): readonly [DiagnosticMessage, ...Type[]] | undefined { + if (isThrowType(type)) type = type.value; + const diag = getTypeOfPropertyOfType(type, "diagnostic" as __String); + if (!diag) return undefined; + const message = getTupleElementType(diag, 0) as StringLiteralType; + if (!message || !(message.flags & TypeFlags.StringLiteral)) return undefined; + if (!Object.prototype.hasOwnProperty.call(Diagnostics, message.value)) return undefined; + // cause error() only accept 4 arguments at most. + return [ + Diagnostics[message.value as keyof typeof Diagnostics], + getTupleElementType(diag, 1) || undefinedType, + getTupleElementType(diag, 2) || undefinedType, + getTupleElementType(diag, 3) || undefinedType, + getTupleElementType(diag, 4) || undefinedType, + ] as const; + } + function getCategoryFromThrowType(type: Type): DiagnosticCategory { + if (isThrowType(type)) type = type.value; + const category = getTypeOfPropertyOfType(type, "category" as __String) as StringLiteralType; + if (!category || !(category.flags & TypeFlags.StringLiteral)) return DiagnosticCategory.Error; + switch (category.value.toLowerCase()) { + case "error": return DiagnosticCategory.Error; + case "suggestion": return DiagnosticCategory.Suggestion; + case "warning": return DiagnosticCategory.Warning; + default: return DiagnosticCategory.Message; + } + } + // function getDeprecatedFromThrowType(type: Type): boolean { + // if (type.flags & TypeFlags.ThrowType) type = (type).value; + // const dep = getTypeOfPropertyOfType(type, <__String>"deprecated"); + // return dep === trueType; + // } + // function getSuggestionFromThrowType(type: Type): string | undefined { + // if (type.flags & TypeFlags.ThrowType) type = (type).value; + // const suggestion = getTypeOfPropertyOfType(type, <__String>"suggestion"); + // if (!suggestion || !(suggestion.flags & TypeFlags.StringLiteral)) return undefined; + // return suggestion.value; + // } + // function getMessageChainFromThrowType(type: Type): Type | undefined { + // if (type.flags & TypeFlags.ThrowType) type = (type).value; + // return getTypeOfPropertyOfType(type, <__String>"next"); + // } + function addErrorOrSuggestion(isError: boolean, diagnostic: Diagnostic) { if (isError) { diagnostics.add(diagnostic); @@ -1205,6 +1284,7 @@ namespace ts { suggestionDiagnostics.add({ ...diagnostic, category: DiagnosticCategory.Suggestion }); } } + /** @deprecated use appendDiagnosticWithCategory */ function errorOrSuggestion(isError: boolean, location: Node, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void { // Pseudo-synthesized input node if (location.pos < 0 || location.end < 0) { @@ -1218,6 +1298,18 @@ namespace ts { } addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); // eslint-disable-line local/no-in-operator } + function appendDiagnosticWithCategory(category: DiagnosticCategory | undefined, location: Node, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void { + const diag = "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message); // eslint-disable-line local/no-in-operator + diag.category = category ?? diag.category; + switch (category) { + case DiagnosticCategory.Error: + case DiagnosticCategory.Warning: + return diagnostics.add(diag); + case DiagnosticCategory.Suggestion: + case DiagnosticCategory.Message: + return suggestionDiagnostics.add(diag); + } + } function errorAndMaybeSuggestAwait( location: Node, @@ -5278,6 +5370,9 @@ namespace ts { if (type.flags & TypeFlags.Substitution) { return typeToTypeNodeHelper((type as SubstitutionType).baseType, context); } + if (type.flags & TypeFlags.ThrowType) { + return typeToTypeNodeHelper(neverType, context); + } return Debug.fail("Should be unreachable."); @@ -12731,11 +12826,14 @@ namespace ts { return (type as UnionType).resolvedReducedType || ((type as UnionType).resolvedReducedType = getReducedUnionType(type as UnionType)); } else if (type.flags & TypeFlags.Intersection) { - if (!((type as IntersectionType).objectFlags & ObjectFlags.IsNeverIntersectionComputed)) { - (type as IntersectionType).objectFlags |= ObjectFlags.IsNeverIntersectionComputed | - (some(getPropertiesOfUnionOrIntersectionType(type as IntersectionType), isNeverReducedProperty) ? ObjectFlags.IsNeverIntersection : 0); + const intersectionType = (type as IntersectionType); + const innerThrowType = find(intersectionType.types, isThrowType); + if (innerThrowType) return innerThrowType; + if (!(intersectionType.objectFlags & ObjectFlags.IsNeverIntersectionComputed)) { + intersectionType.objectFlags |= ObjectFlags.IsNeverIntersectionComputed | + (some(getPropertiesOfUnionOrIntersectionType(intersectionType), isNeverReducedProperty) ? ObjectFlags.IsNeverIntersection : 0); } - return (type as IntersectionType).objectFlags & ObjectFlags.IsNeverIntersection ? neverType : type; + return intersectionType.objectFlags & ObjectFlags.IsNeverIntersection ? neverType : type; } return type; } @@ -15537,6 +15635,9 @@ namespace ts { case SyntaxKind.ReadonlyKeyword: links.resolvedType = getTypeFromTypeNode(node.type); break; + case SyntaxKind.ThrowKeyword: + links.resolvedType = createThrowType(getTypeFromTypeNode(node.type)); + break; default: throw Debug.assertNever(node.operator); } @@ -15633,23 +15734,49 @@ namespace ts { } function getStringMappingType(symbol: Symbol, type: Type): Type { - return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getStringMappingType(symbol, t)) : - // Mapping> === Mapping - type.flags & TypeFlags.StringMapping && symbol === type.symbol ? type : - isGenericIndexType(type) || isPatternLiteralPlaceholderType(type) ? getStringMappingTypeForGenericType(symbol, isPatternLiteralPlaceholderType(type) && !(type.flags & TypeFlags.StringMapping) ? getTemplateLiteralType(["", ""], [type]) : type) : - type.flags & TypeFlags.StringLiteral ? getStringLiteralType(applyStringMapping(symbol, (type as StringLiteralType).value)) : - type.flags & TypeFlags.TemplateLiteral ? getTemplateLiteralType(...applyTemplateStringMapping(symbol, (type as TemplateLiteralType).texts, (type as TemplateLiteralType).types)) : - type; + if (type.flags & (TypeFlags.Union | TypeFlags.Never)) { + return mapType(type, t => getStringMappingType(symbol, t)); + } + // Mapping> === Mapping + else if (type.flags & TypeFlags.StringMapping && symbol === type.symbol) { + return type; + } + else if (isGenericIndexType(type) || isPatternLiteralPlaceholderType(type)) { + return getStringMappingTypeForGenericType(symbol, isPatternLiteralPlaceholderType(type) && !(type.flags & TypeFlags.StringMapping) ? getTemplateLiteralType(["", ""], [type]) : type); + } + else if (type.flags & TypeFlags.StringLiteral) { + return getStringLiteralType(applyStringMapping(symbol, type)); + } + else if (type.flags & TypeFlags.TemplateLiteral) { + return getTemplateLiteralType(...applyTemplateStringMapping(symbol, (type as TemplateLiteralType).texts, (type as TemplateLiteralType).types)); + } + const intrinsicName = intrinsicTypeKinds.get(symbol.escapedName as string); + if (intrinsicName === IntrinsicTypeKind.TypeToString) { + return getStringLiteralType(applyStringMapping(intrinsicName, type)); + } + else { + return type; + } } - function applyStringMapping(symbol: Symbol, str: string) { - switch (intrinsicTypeKinds.get(symbol.escapedName as string)) { + function applyStringMapping(kind: IntrinsicTypeKind | Symbol | undefined, type: Type) { + if (typeof kind === "object") kind = intrinsicTypeKinds.get(kind.escapedName as string); + let str = ""; + if (kind === IntrinsicTypeKind.TypeToString) { + str = typeToString(type); + } + else if (type.flags & TypeFlags.StringLiteral) { + str = (type as StringLiteralType).value; + } + if (!kind) return str; + switch (kind) { case IntrinsicTypeKind.Uppercase: return str.toUpperCase(); case IntrinsicTypeKind.Lowercase: return str.toLowerCase(); case IntrinsicTypeKind.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1); case IntrinsicTypeKind.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1); + case IntrinsicTypeKind.TypeToString: return str; + default: Debug.assertNever(kind); } - return str; } function applyTemplateStringMapping(symbol: Symbol, texts: readonly string[], types: readonly Type[]): [texts: readonly string[], types: readonly Type[]] { @@ -16805,6 +16932,12 @@ namespace ts { return type; } + function createThrowType(containingType: Type) { + const type = createType(TypeFlags.ThrowType) as ThrowType; + type.value = containingType; + return type; + } + function getESSymbolLikeTypeForNode(node: Node) { if (isValidESSymbolDeclaration(node)) { const symbol = isCommonJsExportPropertyAssignment(node) ? getSymbolOfNode((node as BinaryExpression).left) : getSymbolOfNode(node); @@ -17533,6 +17666,10 @@ namespace ts { } return newBaseType.flags & TypeFlags.TypeVariable ? getSubstitutionType(newBaseType, newConstraint) : getIntersectionType([newConstraint, newBaseType]); } + if (isThrowType(type)) { + const errorMessage = instantiateType(type.value, mapper); + return createThrowType(errorMessage); + } return type; } @@ -20019,8 +20156,12 @@ namespace ts { } if (isTypeIdenticalTo(sourceExtends, (target as ConditionalType).extendsType) && (isRelatedTo((source as ConditionalType).checkType, (target as ConditionalType).checkType, RecursionFlags.Both) || isRelatedTo((target as ConditionalType).checkType, (source as ConditionalType).checkType, RecursionFlags.Both))) { - if (result = isRelatedTo(instantiateType(getTrueTypeFromConditionalType(source as ConditionalType), mapper), getTrueTypeFromConditionalType(target as ConditionalType), RecursionFlags.Both, reportErrors)) { - result &= isRelatedTo(getFalseTypeFromConditionalType(source as ConditionalType), getFalseTypeFromConditionalType(target as ConditionalType), RecursionFlags.Both, reportErrors); + const lTrueType = dropThrowTypeInConditionalType(instantiateType(getTrueTypeFromConditionalType(source as ConditionalType), mapper)); + const rTrueType = dropThrowTypeInConditionalType(getTrueTypeFromConditionalType(target as ConditionalType)); + if (result = (lTrueType.flags | rTrueType.flags) & TypeFlags.ThrowType ? Ternary.True : isRelatedTo(lTrueType, rTrueType, RecursionFlags.Both, reportErrors)) { + const lFalseType = dropThrowTypeInConditionalType(getFalseTypeFromConditionalType(source as ConditionalType)); + const rFalseType = dropThrowTypeInConditionalType(getFalseTypeFromConditionalType(target as ConditionalType)); + result &= (lFalseType.flags | rFalseType.flags) & TypeFlags.ThrowType ? Ternary.True : isRelatedTo(lFalseType, rFalseType, RecursionFlags.Both, reportErrors); } if (result) { return result; @@ -22245,6 +22386,7 @@ namespace ts { // results for union and intersection types for performance reasons. function couldContainTypeVariables(type: Type): boolean { const objectFlags = getObjectFlags(type); + if (isThrowType(type)) return couldContainTypeVariables(type.value); if (objectFlags & ObjectFlags.CouldContainTypeVariablesComputed) { return !!(objectFlags & ObjectFlags.CouldContainTypeVariables); } @@ -23148,7 +23290,7 @@ namespace ts { !(right.flags & allTypeFlags) ? left : left.flags & TypeFlags.String ? left : right.flags & TypeFlags.String ? source : left.flags & TypeFlags.TemplateLiteral ? left : right.flags & TypeFlags.TemplateLiteral && isTypeMatchedByTemplateLiteralType(source, right as TemplateLiteralType) ? source : - left.flags & TypeFlags.StringMapping ? left : right.flags & TypeFlags.StringMapping && str === applyStringMapping(right.symbol, str) ? source : + left.flags & TypeFlags.StringMapping ? left : right.flags & TypeFlags.StringMapping && str === applyStringMapping(right.symbol, source) ? source : left.flags & TypeFlags.StringLiteral ? left : right.flags & TypeFlags.StringLiteral && (right as StringLiteralType).value === str ? right : left.flags & TypeFlags.Number ? left : right.flags & TypeFlags.Number ? getNumberLiteralType(+str) : left.flags & TypeFlags.Enum ? left : right.flags & TypeFlags.Enum ? getNumberLiteralType(+str) : @@ -26138,6 +26280,7 @@ namespace ts { checkNestedBlockScopedBinding(node, symbol); let type = getNarrowedTypeOfSymbol(localOrExportSymbol, node); + checkThrowType(node, getReducedType(type)); const assignmentKind = getAssignmentTargetKind(node); if (assignmentKind) { @@ -29593,6 +29736,7 @@ namespace ts { } propType = isThisPropertyAccessInConstructor(node, prop) ? autoType : writing ? getWriteTypeOfSymbol(prop) : getTypeOfSymbol(prop); + checkThrowType(node, propType); } return getFlowTypeOfAccessExpression(node, prop, propType, right, checkMode); @@ -30720,7 +30864,6 @@ namespace ts { reportErrors: boolean, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, ): readonly Diagnostic[] | undefined { - const errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } = { errors: undefined, skipLogging: true }; if (isJsxOpeningLikeElement(node)) { if (!checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, checkMode, reportErrors, containingMessageChain, errorOutputContainer)) { @@ -30757,6 +30900,7 @@ namespace ts { const checkArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType; if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? arg : undefined, arg, headMessage, containingMessageChain, errorOutputContainer)) { Debug.assert(!reportErrors || !!errorOutputContainer.errors, "parameter should have errors when reporting errors"); + checkThrowType(arg, paramType); maybeAddMissingAwaitInfo(arg, checkArgType, paramType); return errorOutputContainer.errors || emptyArray; } @@ -32259,6 +32403,7 @@ namespace ts { } const returnType = getReturnTypeOfSignature(signature); + checkThrowType(node, returnType); // Treat any call to the global 'Symbol' function that is part of a const variable or readonly property // as a fresh unique symbol literal type. if (returnType.flags & TypeFlags.ESSymbolLike && isSymbolOrSymbolForCall(node)) { @@ -36204,6 +36349,7 @@ namespace ts { } forEach(node.typeArguments, checkSourceElement); const type = getTypeFromTypeReference(node); + checkThrowType(node, type); if (!isErrorType(type)) { if (node.typeArguments) { addLazyDiagnostic(() => { @@ -39681,9 +39827,25 @@ namespace ts { if (!returnIterationType) { return errorType; } - return isAsync ? getAwaitedTypeNoAlias(unwrapAwaitedType(returnIterationType)) : returnIterationType; + return dropThrowTypeInConditionalType(isAsync ? getAwaitedTypeNoAlias(unwrapAwaitedType(returnIterationType)) : returnIterationType); + } + return dropThrowTypeInConditionalType(isAsync ? getAwaitedTypeNoAlias(returnType) || errorType : returnType); + } + function dropThrowTypeInConditionalType(type: Type): Type; + function dropThrowTypeInConditionalType(type: Type | undefined): Type | undefined; + function dropThrowTypeInConditionalType(type: Type | undefined): Type | undefined { + if (!type) return type; + let result = type; + while (true) { + if (!(result.flags & TypeFlags.Conditional)) return result; + const left = getTrueTypeFromConditionalType(result as ConditionalType); + const right = getFalseTypeFromConditionalType(result as ConditionalType); + if (isThrowType(left) !== isThrowType(right)) { + if (isThrowType(left)) result = right; + else result = left; + } + else return type; } - return isAsync ? getAwaitedTypeNoAlias(returnType) || errorType : returnType; } function isUnwrappedReturnTypeVoidOrAny(func: SignatureDeclaration, returnType: Type): boolean { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 7a8f3192d917c..ed193e985e17b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3567,6 +3567,10 @@ "category": "Error", "code": 2845 }, + "Type instantiated results in a throw type saying: {0}": { + "category": "Error", + "code": 2899 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 27b2b75e9c7f0..29fc3c49ce7d9 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -2171,7 +2171,7 @@ namespace ts { } // @api - function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode { + function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.ThrowKeyword, type: TypeNode): TypeOperatorNode { const node = createBaseNode(SyntaxKind.TypeOperator); node.operator = operator; node.type = operator === SyntaxKind.ReadonlyKeyword ? diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index bd85362d90409..695dec51b12f0 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4146,6 +4146,7 @@ namespace ts { case SyntaxKind.ReadonlyKeyword: case SyntaxKind.SymbolKeyword: case SyntaxKind.UniqueKeyword: + case SyntaxKind.ThrowKeyword: case SyntaxKind.VoidKeyword: case SyntaxKind.UndefinedKeyword: case SyntaxKind.NullKeyword: @@ -4228,7 +4229,7 @@ namespace ts { return type; } - function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword) { + function parseTypeOperator(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.ThrowKeyword) { const pos = getNodePos(); parseExpected(operator); return finishNode(factory.createTypeOperatorNode(operator, parseTypeOperatorOrHigher()), pos); @@ -4263,6 +4264,7 @@ namespace ts { case SyntaxKind.KeyOfKeyword: case SyntaxKind.UniqueKeyword: case SyntaxKind.ReadonlyKeyword: + case SyntaxKind.ThrowKeyword: return parseTypeOperator(operator); case SyntaxKind.InferKeyword: return parseInferType(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 66e55ab58b381..11b4beb5039a2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2045,7 +2045,7 @@ namespace ts { export interface TypeOperatorNode extends TypeNode { readonly kind: SyntaxKind.TypeOperator; - readonly operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword; + readonly operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.ThrowKeyword; readonly type: TypeNode; } @@ -5605,6 +5605,7 @@ namespace ts { NonPrimitive = 1 << 26, // intrinsic object type TemplateLiteral = 1 << 27, // Template literal type StringMapping = 1 << 28, // Uppercase/Lowercase type + ThrowType = 1 << 29, // throw T /* @internal */ AnyOrUnknown = Any | Unknown, @@ -5745,6 +5746,10 @@ namespace ts { export interface EnumType extends Type { } + export interface ThrowType extends Type { + value: Type; + } + // Types included in TypeFlags.ObjectFlagsType have an objectFlags property. Some ObjectFlags // are specific to certain types and reuse the same bit position. Those ObjectFlags require a check // for a certain TypeFlags value to determine their meaning. @@ -7812,7 +7817,7 @@ namespace ts { createParenthesizedType(type: TypeNode): ParenthesizedTypeNode; updateParenthesizedType(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode; createThisTypeNode(): ThisTypeNode; - createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode; + createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.ThrowKeyword, type: TypeNode): TypeOperatorNode; updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode): TypeOperatorNode; createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 52acb7d347eec..4d054ecd3ba8b 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -1186,6 +1186,7 @@ namespace FourSlashInterface { typeEntry("Lowercase"), typeEntry("Capitalize"), typeEntry("Uncapitalize"), + typeEntry("TypeToString"), interfaceEntry("ThisType"), varEntry("ArrayBuffer"), interfaceEntry("ArrayBufferTypes"), diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 36c4e4fff24b0..3b81fb2af903a 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1630,6 +1630,11 @@ type Capitalize = intrinsic; */ type Uncapitalize = intrinsic; +/** + * Get a string representation of a type + */ +type TypeToString = intrinsic; + /** * Marker for contextual 'this' type */ diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 575116ceed587..12ad44a43adc5 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -992,7 +992,7 @@ declare namespace ts { } export interface TypeOperatorNode extends TypeNode { readonly kind: SyntaxKind.TypeOperator; - readonly operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword; + readonly operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.ThrowKeyword; readonly type: TypeNode; } export interface IndexedAccessTypeNode extends TypeNode { @@ -2665,6 +2665,7 @@ declare namespace ts { NonPrimitive = 67108864, TemplateLiteral = 134217728, StringMapping = 268435456, + ThrowType = 536870912, Literal = 2944, Unit = 109440, StringOrNumberLiteral = 384, @@ -2713,6 +2714,9 @@ declare namespace ts { } export interface EnumType extends Type { } + export interface ThrowType extends Type { + value: Type; + } export enum ObjectFlags { Class = 1, Interface = 2, @@ -3518,7 +3522,7 @@ declare namespace ts { createParenthesizedType(type: TypeNode): ParenthesizedTypeNode; updateParenthesizedType(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode; createThisTypeNode(): ThisTypeNode; - createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode; + createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.ThrowKeyword, type: TypeNode): TypeOperatorNode; updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode): TypeOperatorNode; createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 0e6b2559af02e..3e9f473bbb03a 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -992,7 +992,7 @@ declare namespace ts { } export interface TypeOperatorNode extends TypeNode { readonly kind: SyntaxKind.TypeOperator; - readonly operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword; + readonly operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.ThrowKeyword; readonly type: TypeNode; } export interface IndexedAccessTypeNode extends TypeNode { @@ -2665,6 +2665,7 @@ declare namespace ts { NonPrimitive = 67108864, TemplateLiteral = 134217728, StringMapping = 268435456, + ThrowType = 536870912, Literal = 2944, Unit = 109440, StringOrNumberLiteral = 384, @@ -2713,6 +2714,9 @@ declare namespace ts { } export interface EnumType extends Type { } + export interface ThrowType extends Type { + value: Type; + } export enum ObjectFlags { Class = 1, Interface = 2, @@ -3518,7 +3522,7 @@ declare namespace ts { createParenthesizedType(type: TypeNode): ParenthesizedTypeNode; updateParenthesizedType(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode; createThisTypeNode(): ThisTypeNode; - createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode; + createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.ThrowKeyword, type: TypeNode): TypeOperatorNode; updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode): TypeOperatorNode; createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; diff --git a/tests/baselines/reference/throwType_function_parameter.errors.txt b/tests/baselines/reference/throwType_function_parameter.errors.txt new file mode 100644 index 0000000000000..b8e3e85936906 --- /dev/null +++ b/tests/baselines/reference/throwType_function_parameter.errors.txt @@ -0,0 +1,33 @@ +tests/cases/compiler/throwType_function_parameter.ts(4,24): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. +tests/cases/compiler/throwType_function_parameter.ts(4,24): error TS2899: Type instantiated results in a throw type saying: + No zero +tests/cases/compiler/throwType_function_parameter.ts(13,4): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. +tests/cases/compiler/throwType_function_parameter.ts(13,4): error TS2899: Type instantiated results in a throw type saying: + "found ""str"" + + +==== tests/cases/compiler/throwType_function_parameter.ts (4 errors) ==== + function checkParameterPosition(y: T extends 1234 ? throw 'No zero' : T) { + y.toExponential() + } + checkParameterPosition(1234) + ~~~~ +!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. + ~~~~ +!!! error TS2899: Type instantiated results in a throw type saying: +!!! error TS2899: No zero + checkParameterPosition(12345678) + + type MustNumber = T extends number ? T : throw `"found "${TypeToString}"` + type MustNumber2 = T extends number ? T : throw `"found "${TypeToString}"` + function f2(a: MustNumber, b: MustNumber2) { + a = b + } + + f2('str', {}) + ~~~~~ +!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. + ~~~~~ +!!! error TS2899: Type instantiated results in a throw type saying: +!!! error TS2899: "found ""str"" + \ No newline at end of file diff --git a/tests/baselines/reference/throwType_function_parameter.js b/tests/baselines/reference/throwType_function_parameter.js new file mode 100644 index 0000000000000..793fd77b0d436 --- /dev/null +++ b/tests/baselines/reference/throwType_function_parameter.js @@ -0,0 +1,26 @@ +//// [throwType_function_parameter.ts] +function checkParameterPosition(y: T extends 1234 ? throw 'No zero' : T) { + y.toExponential() +} +checkParameterPosition(1234) +checkParameterPosition(12345678) + +type MustNumber = T extends number ? T : throw `"found "${TypeToString}"` +type MustNumber2 = T extends number ? T : throw `"found "${TypeToString}"` +function f2(a: MustNumber, b: MustNumber2) { + a = b +} + +f2('str', {}) + + +//// [throwType_function_parameter.js] +function checkParameterPosition(y) { + y.toExponential(); +} +checkParameterPosition(1234); +checkParameterPosition(12345678); +function f2(a, b) { + a = b; +} +f2('str', {}); diff --git a/tests/baselines/reference/throwType_function_parameter.symbols b/tests/baselines/reference/throwType_function_parameter.symbols new file mode 100644 index 0000000000000..9f1a09f4a6e00 --- /dev/null +++ b/tests/baselines/reference/throwType_function_parameter.symbols @@ -0,0 +1,53 @@ +=== tests/cases/compiler/throwType_function_parameter.ts === +function checkParameterPosition(y: T extends 1234 ? throw 'No zero' : T) { +>checkParameterPosition : Symbol(checkParameterPosition, Decl(throwType_function_parameter.ts, 0, 0)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 0, 32)) +>y : Symbol(y, Decl(throwType_function_parameter.ts, 0, 50)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 0, 32)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 0, 32)) + + y.toExponential() +>y.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>y : Symbol(y, Decl(throwType_function_parameter.ts, 0, 50)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +} +checkParameterPosition(1234) +>checkParameterPosition : Symbol(checkParameterPosition, Decl(throwType_function_parameter.ts, 0, 0)) + +checkParameterPosition(12345678) +>checkParameterPosition : Symbol(checkParameterPosition, Decl(throwType_function_parameter.ts, 0, 0)) + +type MustNumber = T extends number ? T : throw `"found "${TypeToString}"` +>MustNumber : Symbol(MustNumber, Decl(throwType_function_parameter.ts, 4, 32)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 6, 16)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 6, 16)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 6, 16)) +>TypeToString : Symbol(TypeToString, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 6, 16)) + +type MustNumber2 = T extends number ? T : throw `"found "${TypeToString}"` +>MustNumber2 : Symbol(MustNumber2, Decl(throwType_function_parameter.ts, 6, 79)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 7, 17)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 7, 17)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 7, 17)) +>TypeToString : Symbol(TypeToString, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 7, 17)) + +function f2(a: MustNumber, b: MustNumber2) { +>f2 : Symbol(f2, Decl(throwType_function_parameter.ts, 7, 80)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 8, 12)) +>a : Symbol(a, Decl(throwType_function_parameter.ts, 8, 15)) +>MustNumber : Symbol(MustNumber, Decl(throwType_function_parameter.ts, 4, 32)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 8, 12)) +>b : Symbol(b, Decl(throwType_function_parameter.ts, 8, 32)) +>MustNumber2 : Symbol(MustNumber2, Decl(throwType_function_parameter.ts, 6, 79)) +>T : Symbol(T, Decl(throwType_function_parameter.ts, 8, 12)) + + a = b +>a : Symbol(a, Decl(throwType_function_parameter.ts, 8, 15)) +>b : Symbol(b, Decl(throwType_function_parameter.ts, 8, 32)) +} + +f2('str', {}) +>f2 : Symbol(f2, Decl(throwType_function_parameter.ts, 7, 80)) + diff --git a/tests/baselines/reference/throwType_function_parameter.types b/tests/baselines/reference/throwType_function_parameter.types new file mode 100644 index 0000000000000..b96de39fb718c --- /dev/null +++ b/tests/baselines/reference/throwType_function_parameter.types @@ -0,0 +1,44 @@ +=== tests/cases/compiler/throwType_function_parameter.ts === +function checkParameterPosition(y: T extends 1234 ? throw 'No zero' : T) { +>checkParameterPosition : (y: T extends 1234 ? throw 'No zero' : T) => void +>y : T extends 1234 ? never : T + + y.toExponential() +>y.toExponential() : string +>y.toExponential : (fractionDigits?: number) => string +>y : T extends 1234 ? never : T +>toExponential : (fractionDigits?: number) => string +} +checkParameterPosition(1234) +>checkParameterPosition(1234) : void +>checkParameterPosition : (y: T extends 1234 ? never : T) => void +>1234 : 1234 + +checkParameterPosition(12345678) +>checkParameterPosition(12345678) : void +>checkParameterPosition : (y: T extends 1234 ? never : T) => void +>12345678 : 12345678 + +type MustNumber = T extends number ? T : throw `"found "${TypeToString}"` +>MustNumber : MustNumber + +type MustNumber2 = T extends number ? T : throw `"found "${TypeToString}"` +>MustNumber2 : MustNumber2 + +function f2(a: MustNumber, b: MustNumber2) { +>f2 : (a: MustNumber, b: MustNumber2) => void +>a : MustNumber +>b : MustNumber2 + + a = b +>a = b : MustNumber2 +>a : MustNumber +>b : MustNumber2 +} + +f2('str', {}) +>f2('str', {}) : void +>f2 : (a: MustNumber, b: MustNumber2) => void +>'str' : "str" +>{} : {} + diff --git a/tests/baselines/reference/throwType_function_return.errors.txt b/tests/baselines/reference/throwType_function_return.errors.txt new file mode 100644 index 0000000000000..8411065bd0f32 --- /dev/null +++ b/tests/baselines/reference/throwType_function_return.errors.txt @@ -0,0 +1,24 @@ +tests/cases/compiler/throwType_function_return.ts(5,1): error TS2899: Type instantiated results in a throw type saying: + Cannot divided by 0 +tests/cases/compiler/throwType_function_return.ts(10,1): error TS2899: Type instantiated results in a throw type saying: + Wrong + + +==== tests/cases/compiler/throwType_function_return.ts (2 errors) ==== + function checkedDivide(x: T): T extends 0 ? throw 'Cannot divided by 0' : number { + if (x === 0) throw new Error('') + return 5 / x + } + checkedDivide(0) + ~~~~~~~~~~~~~~~~ +!!! error TS2899: Type instantiated results in a throw type saying: +!!! error TS2899: Cannot divided by 0 + checkedDivide(1) + + const theAnswerToEverything = (x: T): T extends 42 ? T : throw "Wrong" => x + theAnswerToEverything(42 as const) + theAnswerToEverything('') + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2899: Type instantiated results in a throw type saying: +!!! error TS2899: Wrong + \ No newline at end of file diff --git a/tests/baselines/reference/throwType_function_return.js b/tests/baselines/reference/throwType_function_return.js new file mode 100644 index 0000000000000..4d24c73aecf43 --- /dev/null +++ b/tests/baselines/reference/throwType_function_return.js @@ -0,0 +1,24 @@ +//// [throwType_function_return.ts] +function checkedDivide(x: T): T extends 0 ? throw 'Cannot divided by 0' : number { + if (x === 0) throw new Error('') + return 5 / x +} +checkedDivide(0) +checkedDivide(1) + +const theAnswerToEverything = (x: T): T extends 42 ? T : throw "Wrong" => x +theAnswerToEverything(42 as const) +theAnswerToEverything('') + + +//// [throwType_function_return.js] +function checkedDivide(x) { + if (x === 0) + throw new Error(''); + return 5 / x; +} +checkedDivide(0); +checkedDivide(1); +var theAnswerToEverything = function (x) { return x; }; +theAnswerToEverything(42); +theAnswerToEverything(''); diff --git a/tests/baselines/reference/throwType_function_return.symbols b/tests/baselines/reference/throwType_function_return.symbols new file mode 100644 index 0000000000000..617afab04c6e7 --- /dev/null +++ b/tests/baselines/reference/throwType_function_return.symbols @@ -0,0 +1,37 @@ +=== tests/cases/compiler/throwType_function_return.ts === +function checkedDivide(x: T): T extends 0 ? throw 'Cannot divided by 0' : number { +>checkedDivide : Symbol(checkedDivide, Decl(throwType_function_return.ts, 0, 0)) +>T : Symbol(T, Decl(throwType_function_return.ts, 0, 23)) +>x : Symbol(x, Decl(throwType_function_return.ts, 0, 41)) +>T : Symbol(T, Decl(throwType_function_return.ts, 0, 23)) +>T : Symbol(T, Decl(throwType_function_return.ts, 0, 23)) + + if (x === 0) throw new Error('') +>x : Symbol(x, Decl(throwType_function_return.ts, 0, 41)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + return 5 / x +>x : Symbol(x, Decl(throwType_function_return.ts, 0, 41)) +} +checkedDivide(0) +>checkedDivide : Symbol(checkedDivide, Decl(throwType_function_return.ts, 0, 0)) + +checkedDivide(1) +>checkedDivide : Symbol(checkedDivide, Decl(throwType_function_return.ts, 0, 0)) + +const theAnswerToEverything = (x: T): T extends 42 ? T : throw "Wrong" => x +>theAnswerToEverything : Symbol(theAnswerToEverything, Decl(throwType_function_return.ts, 7, 5)) +>T : Symbol(T, Decl(throwType_function_return.ts, 7, 31)) +>x : Symbol(x, Decl(throwType_function_return.ts, 7, 34)) +>T : Symbol(T, Decl(throwType_function_return.ts, 7, 31)) +>T : Symbol(T, Decl(throwType_function_return.ts, 7, 31)) +>T : Symbol(T, Decl(throwType_function_return.ts, 7, 31)) +>x : Symbol(x, Decl(throwType_function_return.ts, 7, 34)) + +theAnswerToEverything(42 as const) +>theAnswerToEverything : Symbol(theAnswerToEverything, Decl(throwType_function_return.ts, 7, 5)) +>const : Symbol(const) + +theAnswerToEverything('') +>theAnswerToEverything : Symbol(theAnswerToEverything, Decl(throwType_function_return.ts, 7, 5)) + diff --git a/tests/baselines/reference/throwType_function_return.types b/tests/baselines/reference/throwType_function_return.types new file mode 100644 index 0000000000000..572590f407454 --- /dev/null +++ b/tests/baselines/reference/throwType_function_return.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/throwType_function_return.ts === +function checkedDivide(x: T): T extends 0 ? throw 'Cannot divided by 0' : number { +>checkedDivide : (x: T) => T extends 0 ? throw 'Cannot divided by 0' : number +>x : T + + if (x === 0) throw new Error('') +>x === 0 : boolean +>x : T +>0 : 0 +>new Error('') : Error +>Error : ErrorConstructor +>'' : "" + + return 5 / x +>5 / x : number +>5 : 5 +>x : T +} +checkedDivide(0) +>checkedDivide(0) : never +>checkedDivide : (x: T) => T extends 0 ? never : number +>0 : 0 + +checkedDivide(1) +>checkedDivide(1) : number +>checkedDivide : (x: T) => T extends 0 ? never : number +>1 : 1 + +const theAnswerToEverything = (x: T): T extends 42 ? T : throw "Wrong" => x +>theAnswerToEverything : (x: T) => T extends 42 ? T : never +>(x: T): T extends 42 ? T : throw "Wrong" => x : (x: T) => T extends 42 ? T : never +>x : T +>x : T + +theAnswerToEverything(42 as const) +>theAnswerToEverything(42 as const) : 42 +>theAnswerToEverything : (x: T) => T extends 42 ? T : never +>42 as const : 42 +>42 : 42 + +theAnswerToEverything('') +>theAnswerToEverything('') : never +>theAnswerToEverything : (x: T) => T extends 42 ? T : never +>'' : "" + diff --git a/tests/baselines/reference/throwType_identifier.errors.txt b/tests/baselines/reference/throwType_identifier.errors.txt new file mode 100644 index 0000000000000..4b8ba1fe3e716 --- /dev/null +++ b/tests/baselines/reference/throwType_identifier.errors.txt @@ -0,0 +1,11 @@ +tests/cases/compiler/throwType_identifier.ts(2,13): error TS2899: Type instantiated results in a throw type saying: + please use window.name + + +==== tests/cases/compiler/throwType_identifier.ts (1 errors) ==== + declare const name: throw 'please use window.name' + console.log(name, 123, window.name) + ~~~~ +!!! error TS2899: Type instantiated results in a throw type saying: +!!! error TS2899: please use window.name + export {} \ No newline at end of file diff --git a/tests/baselines/reference/throwType_identifier.js b/tests/baselines/reference/throwType_identifier.js new file mode 100644 index 0000000000000..0f54f73f3f5d8 --- /dev/null +++ b/tests/baselines/reference/throwType_identifier.js @@ -0,0 +1,9 @@ +//// [throwType_identifier.ts] +declare const name: throw 'please use window.name' +console.log(name, 123, window.name) +export {} + +//// [throwType_identifier.js] +"use strict"; +exports.__esModule = true; +console.log(name, 123, window.name); diff --git a/tests/baselines/reference/throwType_identifier.symbols b/tests/baselines/reference/throwType_identifier.symbols new file mode 100644 index 0000000000000..92c3560cdfd32 --- /dev/null +++ b/tests/baselines/reference/throwType_identifier.symbols @@ -0,0 +1,14 @@ +=== tests/cases/compiler/throwType_identifier.ts === +declare const name: throw 'please use window.name' +>name : Symbol(name, Decl(throwType_identifier.ts, 0, 13)) + +console.log(name, 123, window.name) +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>name : Symbol(name, Decl(throwType_identifier.ts, 0, 13)) +>window.name : Symbol(Window.name, Decl(lib.dom.d.ts, --, --)) +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) +>name : Symbol(Window.name, Decl(lib.dom.d.ts, --, --)) + +export {} diff --git a/tests/baselines/reference/throwType_identifier.types b/tests/baselines/reference/throwType_identifier.types new file mode 100644 index 0000000000000..6962a79109e58 --- /dev/null +++ b/tests/baselines/reference/throwType_identifier.types @@ -0,0 +1,16 @@ +=== tests/cases/compiler/throwType_identifier.ts === +declare const name: throw 'please use window.name' +>name : never + +console.log(name, 123, window.name) +>console.log(name, 123, window.name) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>name : never +>123 : 123 +>window.name : string +>window : Window & typeof globalThis +>name : string + +export {} diff --git a/tests/baselines/reference/throwType_property_access.errors.txt b/tests/baselines/reference/throwType_property_access.errors.txt new file mode 100644 index 0000000000000..7cb4c2cddd22a --- /dev/null +++ b/tests/baselines/reference/throwType_property_access.errors.txt @@ -0,0 +1,12 @@ +tests/cases/compiler/throwType_property_access.ts(4,1): error TS2899: Type instantiated results in a throw type saying: + + + +==== tests/cases/compiler/throwType_property_access.ts (1 errors) ==== + class X { + i: T extends number ? throw '' : string = {} as any + } + new X<0>().i + ~~~~~~~~~~~~ +!!! error TS2899: Type instantiated results in a throw type saying: +!!! error TS2899: \ No newline at end of file diff --git a/tests/baselines/reference/throwType_property_access.js b/tests/baselines/reference/throwType_property_access.js new file mode 100644 index 0000000000000..f5e00cf291e2e --- /dev/null +++ b/tests/baselines/reference/throwType_property_access.js @@ -0,0 +1,14 @@ +//// [throwType_property_access.ts] +class X { + i: T extends number ? throw '' : string = {} as any +} +new X<0>().i + +//// [throwType_property_access.js] +var X = /** @class */ (function () { + function X() { + this.i = {}; + } + return X; +}()); +new X().i; diff --git a/tests/baselines/reference/throwType_property_access.symbols b/tests/baselines/reference/throwType_property_access.symbols new file mode 100644 index 0000000000000..ea1bea76634fd --- /dev/null +++ b/tests/baselines/reference/throwType_property_access.symbols @@ -0,0 +1,14 @@ +=== tests/cases/compiler/throwType_property_access.ts === +class X { +>X : Symbol(X, Decl(throwType_property_access.ts, 0, 0)) +>T : Symbol(T, Decl(throwType_property_access.ts, 0, 8)) + + i: T extends number ? throw '' : string = {} as any +>i : Symbol(X.i, Decl(throwType_property_access.ts, 0, 12)) +>T : Symbol(T, Decl(throwType_property_access.ts, 0, 8)) +} +new X<0>().i +>new X<0>().i : Symbol(X.i, Decl(throwType_property_access.ts, 0, 12)) +>X : Symbol(X, Decl(throwType_property_access.ts, 0, 0)) +>i : Symbol(X.i, Decl(throwType_property_access.ts, 0, 12)) + diff --git a/tests/baselines/reference/throwType_property_access.types b/tests/baselines/reference/throwType_property_access.types new file mode 100644 index 0000000000000..e9a8f1c41c8f4 --- /dev/null +++ b/tests/baselines/reference/throwType_property_access.types @@ -0,0 +1,15 @@ +=== tests/cases/compiler/throwType_property_access.ts === +class X { +>X : X + + i: T extends number ? throw '' : string = {} as any +>i : T extends number ? never : string +>{} as any : any +>{} : {} +} +new X<0>().i +>new X<0>().i : never +>new X<0>() : X<0> +>X : typeof X +>i : never + diff --git a/tests/baselines/reference/throwType_type_reference.errors.txt b/tests/baselines/reference/throwType_type_reference.errors.txt new file mode 100644 index 0000000000000..1211126badf5c --- /dev/null +++ b/tests/baselines/reference/throwType_type_reference.errors.txt @@ -0,0 +1,12 @@ +tests/cases/compiler/throwType_type_reference.ts(3,10): error TS2899: Type instantiated results in a throw type saying: + Expected number, but found ""Window & typeof globalThis"" + + +==== tests/cases/compiler/throwType_type_reference.ts (1 errors) ==== + type MustNumber = T extends number ? T : throw `Expected number, but found "${TypeToString}"` + type A = MustNumber<1> + type B = MustNumber> + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2899: Type instantiated results in a throw type saying: +!!! error TS2899: Expected number, but found ""Window & typeof globalThis"" + \ No newline at end of file diff --git a/tests/baselines/reference/throwType_type_reference.js b/tests/baselines/reference/throwType_type_reference.js new file mode 100644 index 0000000000000..f243935286360 --- /dev/null +++ b/tests/baselines/reference/throwType_type_reference.js @@ -0,0 +1,7 @@ +//// [throwType_type_reference.ts] +type MustNumber = T extends number ? T : throw `Expected number, but found "${TypeToString}"` +type A = MustNumber<1> +type B = MustNumber> + + +//// [throwType_type_reference.js] diff --git a/tests/baselines/reference/throwType_type_reference.symbols b/tests/baselines/reference/throwType_type_reference.symbols new file mode 100644 index 0000000000000..b425d843ec655 --- /dev/null +++ b/tests/baselines/reference/throwType_type_reference.symbols @@ -0,0 +1,19 @@ +=== tests/cases/compiler/throwType_type_reference.ts === +type MustNumber = T extends number ? T : throw `Expected number, but found "${TypeToString}"` +>MustNumber : Symbol(MustNumber, Decl(throwType_type_reference.ts, 0, 0)) +>T : Symbol(T, Decl(throwType_type_reference.ts, 0, 16)) +>T : Symbol(T, Decl(throwType_type_reference.ts, 0, 16)) +>T : Symbol(T, Decl(throwType_type_reference.ts, 0, 16)) +>TypeToString : Symbol(TypeToString, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(throwType_type_reference.ts, 0, 16)) + +type A = MustNumber<1> +>A : Symbol(A, Decl(throwType_type_reference.ts, 0, 99)) +>MustNumber : Symbol(MustNumber, Decl(throwType_type_reference.ts, 0, 0)) + +type B = MustNumber> +>B : Symbol(B, Decl(throwType_type_reference.ts, 1, 22)) +>MustNumber : Symbol(MustNumber, Decl(throwType_type_reference.ts, 0, 0)) +>TypeToString : Symbol(TypeToString, Decl(lib.es5.d.ts, --, --)) +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) + diff --git a/tests/baselines/reference/throwType_type_reference.types b/tests/baselines/reference/throwType_type_reference.types new file mode 100644 index 0000000000000..debf6043ab71e --- /dev/null +++ b/tests/baselines/reference/throwType_type_reference.types @@ -0,0 +1,11 @@ +=== tests/cases/compiler/throwType_type_reference.ts === +type MustNumber = T extends number ? T : throw `Expected number, but found "${TypeToString}"` +>MustNumber : MustNumber + +type A = MustNumber<1> +>A : 1 + +type B = MustNumber> +>B : never +>window : Window & typeof globalThis + diff --git a/tests/cases/compiler/throwType_function_parameter.ts b/tests/cases/compiler/throwType_function_parameter.ts new file mode 100644 index 0000000000000..37f99475bcadd --- /dev/null +++ b/tests/cases/compiler/throwType_function_parameter.ts @@ -0,0 +1,13 @@ +function checkParameterPosition(y: T extends 1234 ? throw 'No zero' : T) { + y.toExponential() +} +checkParameterPosition(1234) +checkParameterPosition(12345678) + +type MustNumber = T extends number ? T : throw `"found "${TypeToString}"` +type MustNumber2 = T extends number ? T : throw `"found "${TypeToString}"` +function f2(a: MustNumber, b: MustNumber2) { + a = b +} + +f2('str', {}) diff --git a/tests/cases/compiler/throwType_function_return.ts b/tests/cases/compiler/throwType_function_return.ts new file mode 100644 index 0000000000000..374bb69984e69 --- /dev/null +++ b/tests/cases/compiler/throwType_function_return.ts @@ -0,0 +1,10 @@ +function checkedDivide(x: T): T extends 0 ? throw 'Cannot divided by 0' : number { + if (x === 0) throw new Error('') + return 5 / x +} +checkedDivide(0) +checkedDivide(1) + +const theAnswerToEverything = (x: T): T extends 42 ? T : throw "Wrong" => x +theAnswerToEverything(42 as const) +theAnswerToEverything('') diff --git a/tests/cases/compiler/throwType_identifier.ts b/tests/cases/compiler/throwType_identifier.ts new file mode 100644 index 0000000000000..3244d0abca18f --- /dev/null +++ b/tests/cases/compiler/throwType_identifier.ts @@ -0,0 +1,3 @@ +declare const name: throw 'please use window.name' +console.log(name, 123, window.name) +export {} \ No newline at end of file diff --git a/tests/cases/compiler/throwType_property_access.ts b/tests/cases/compiler/throwType_property_access.ts new file mode 100644 index 0000000000000..f9412780d1de6 --- /dev/null +++ b/tests/cases/compiler/throwType_property_access.ts @@ -0,0 +1,5 @@ + +class X { + i: T extends number ? throw '' : string = {} as any +} +new X<0>().i \ No newline at end of file diff --git a/tests/cases/compiler/throwType_type_reference.ts b/tests/cases/compiler/throwType_type_reference.ts new file mode 100644 index 0000000000000..46444690da3df --- /dev/null +++ b/tests/cases/compiler/throwType_type_reference.ts @@ -0,0 +1,3 @@ +type MustNumber = T extends number ? T : throw `Expected number, but found "${TypeToString}"` +type A = MustNumber<1> +type B = MustNumber>