Optional elements in tuple types + empty tuple types + other fixes.

This commit is contained in:
Anders Hejlsberg
2018-06-09 12:19:11 -07:00
parent 43bfccfd08
commit 2e04322a6d
9 changed files with 205 additions and 158 deletions
+1
View File
@@ -3601,6 +3601,7 @@ namespace ts {
case SyntaxKind.TypeLiteral:
case SyntaxKind.ArrayType:
case SyntaxKind.TupleType:
case SyntaxKind.OptionalType:
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
case SyntaxKind.ConditionalType:
+150 -139
View File
@@ -352,7 +352,7 @@ namespace ts {
}
};
const tupleTypes: GenericType[] = [];
const tupleTypes = createMap<GenericType>();
const unionTypes = createMap<UnionType>();
const intersectionTypes = createMap<IntersectionType>();
const literalTypes = createMap<LiteralType>();
@@ -3396,8 +3396,12 @@ namespace ts {
}
else if (type.target.objectFlags & ObjectFlags.Tuple) {
if (typeArguments.length > 0) {
const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, getTypeReferenceArity(type)), context);
const arity = getTypeReferenceArity(type);
const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, arity), context);
if (tupleConstituentNodes && tupleConstituentNodes.length > 0) {
for (let i = (<TupleType>type.target).minLength; i < arity; i++) {
tupleConstituentNodes[i] = createOptionalTypeNode(tupleConstituentNodes[i]);
}
return createTupleTypeNode(tupleConstituentNodes);
}
}
@@ -5406,7 +5410,7 @@ namespace ts {
function getBaseTypes(type: InterfaceType): BaseType[] {
if (!type.resolvedBaseTypes) {
if (type.objectFlags & ObjectFlags.Tuple) {
type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters!))];
type.resolvedBaseTypes = [createArrayType(getUnionType(type.typeParameters || emptyArray))];
}
else if (type.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
if (type.symbol.flags & SymbolFlags.Class) {
@@ -7663,32 +7667,40 @@ namespace ts {
let inferences: Type[] | undefined;
if (typeParameter.symbol) {
for (const declaration of typeParameter.symbol.declarations) {
// When an 'infer T' declaration is immediately contained in a type reference node
// (such as 'Foo<infer T>'), T's constraint is inferred from the constraint of the
// corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are
// present, we form an intersection of the inferred constraint types.
if (declaration.parent.kind === SyntaxKind.InferType && declaration.parent.parent.kind === SyntaxKind.TypeReference) {
const typeReference = <TypeReferenceNode>declaration.parent.parent;
const typeParameters = getTypeParametersForTypeReference(typeReference);
if (typeParameters) {
const index = typeReference.typeArguments!.indexOf(<TypeNode>declaration.parent);
if (index < typeParameters.length) {
const declaredConstraint = getConstraintOfTypeParameter(typeParameters[index]);
if (declaredConstraint) {
// Type parameter constraints can reference other type parameters so
// constraints need to be instantiated. If instantiation produces the
// type parameter itself, we discard that inference. For example, in
// type Foo<T extends string, U extends T> = [T, U];
// type Bar<T> = T extends Foo<infer X, infer X> ? Foo<X, X> : T;
// the instantiated constraint for U is X, so we discard that inference.
const mapper = createTypeMapper(typeParameters, getEffectiveTypeArguments(typeReference, typeParameters));
const constraint = instantiateType(declaredConstraint, mapper);
if (constraint !== typeParameter) {
inferences = append(inferences, constraint);
if (declaration.parent.kind === SyntaxKind.InferType) {
// When an 'infer T' declaration is immediately contained in a type reference node
// (such as 'Foo<infer T>'), T's constraint is inferred from the constraint of the
// corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are
// present, we form an intersection of the inferred constraint types.
const grandParent = declaration.parent.parent;
if (grandParent.kind === SyntaxKind.TypeReference) {
const typeReference = <TypeReferenceNode>grandParent;
const typeParameters = getTypeParametersForTypeReference(typeReference);
if (typeParameters) {
const index = typeReference.typeArguments!.indexOf(<TypeNode>declaration.parent);
if (index < typeParameters.length) {
const declaredConstraint = getConstraintOfTypeParameter(typeParameters[index]);
if (declaredConstraint) {
// Type parameter constraints can reference other type parameters so
// constraints need to be instantiated. If instantiation produces the
// type parameter itself, we discard that inference. For example, in
// type Foo<T extends string, U extends T> = [T, U];
// type Bar<T> = T extends Foo<infer X, infer X> ? Foo<X, X> : T;
// the instantiated constraint for U is X, so we discard that inference.
const mapper = createTypeMapper(typeParameters, getEffectiveTypeArguments(typeReference, typeParameters));
const constraint = instantiateType(declaredConstraint, mapper);
if (constraint !== typeParameter) {
inferences = append(inferences, constraint);
}
}
}
}
}
// When an 'infer T' declaration is immediately contained in a rest parameter
// declaration, we infer an 'unknown[]' constraint.
else if (grandParent.kind === SyntaxKind.Parameter && (<ParameterDeclaration>grandParent).dotDotDotToken) {
inferences = append(inferences, createArrayType(unknownType));
}
}
}
}
@@ -8242,20 +8254,23 @@ namespace ts {
//
// Note that the generic type created by this function has no symbol associated with it. The same
// is true for each of the synthesized type parameters.
function createTupleTypeOfArity(arity: number): GenericType {
const typeParameters: TypeParameter[] = [];
function createTupleTypeOfArity(arity: number, minLength: number): TupleType {
let typeParameters: TypeParameter[] | undefined;
const properties: Symbol[] = [];
for (let i = 0; i < arity; i++) {
const typeParameter = <TypeParameter>createType(TypeFlags.TypeParameter);
typeParameters.push(typeParameter);
const property = createSymbol(SymbolFlags.Property, "" + i as __String);
property.type = typeParameter;
properties.push(property);
if (arity) {
typeParameters = new Array(arity);
for (let i = 0; i < arity; i++) {
const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0), "" + i as __String);
property.type = typeParameters[i] = <TypeParameter>createType(TypeFlags.TypeParameter);
properties.push(property);
}
}
const literalTypes = [];
for (let i = minLength; i <= arity; i++) literalTypes.push(getLiteralType(i));
const lengthSymbol = createSymbol(SymbolFlags.Property, "length" as __String);
lengthSymbol.type = getLiteralType(arity);
lengthSymbol.type = getUnionType(literalTypes);
properties.push(lengthSymbol);
const type = <GenericType & InterfaceTypeWithDeclaredMembers>createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference);
const type = <TupleType & InterfaceTypeWithDeclaredMembers>createObjectType(ObjectFlags.Tuple | ObjectFlags.Reference);
type.typeParameters = typeParameters;
type.outerTypeParameters = undefined;
type.localTypeParameters = typeParameters;
@@ -8271,25 +8286,38 @@ namespace ts {
type.declaredConstructSignatures = emptyArray;
type.declaredStringIndexInfo = undefined;
type.declaredNumberIndexInfo = undefined;
type.minLength = minLength;
return type;
}
function getTupleTypeOfArity(arity: number): GenericType {
return tupleTypes[arity] || (tupleTypes[arity] = createTupleTypeOfArity(arity));
function getTupleTypeOfArity(arity: number, minLength: number): GenericType {
const key = arity + "," + minLength;
let type = tupleTypes.get(key);
if (!type) {
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength));
}
return type;
}
function createTupleType(elementTypes: Type[]) {
return createTypeReference(getTupleTypeOfArity(elementTypes.length), elementTypes);
function createTupleType(elementTypes: Type[], minLength = elementTypes.length) {
const tupleType = getTupleTypeOfArity(elementTypes.length, minLength);
return elementTypes.length ? createTypeReference(tupleType, elementTypes) : tupleType;
}
function getTypeFromTupleTypeNode(node: TupleTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode));
const minLength = findLastIndex(node.elementTypes, n => n.kind !== SyntaxKind.OptionalType) + 1;
links.resolvedType = createTupleType(map(node.elementTypes, getTypeFromTypeNode), minLength);
}
return links.resolvedType;
}
function getTypeFromOptionalTypeNode(node: OptionalTypeNode): Type {
const type = getTypeFromTypeNode(node.type);
return strictNullChecks ? getOptionalType(type) : type;
}
function getTypeId(type: Type) {
return type.id;
}
@@ -9480,6 +9508,8 @@ namespace ts {
return getTypeFromArrayTypeNode(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return getTypeFromTupleTypeNode(<TupleTypeNode>node);
case SyntaxKind.OptionalType:
return getTypeFromOptionalTypeNode(<OptionalTypeNode>node);
case SyntaxKind.UnionType:
return getTypeFromUnionTypeNode(<UnionTypeNode>node);
case SyntaxKind.IntersectionType:
@@ -10100,11 +10130,16 @@ namespace ts {
const sourceCount = getParameterCount(source);
const targetCount = getParameterCount(target);
if (!hasEffectiveRestParameter(target) && getMinArgumentCount(source) > targetCount) {
return Ternary.False;
}
const sourceRestTypeParameter = getRestTypeParameter(source);
const targetRestTypeParameter = sourceRestTypeParameter ? getRestTypeParameter(target) : undefined;
if (sourceRestTypeParameter && !(targetRestTypeParameter && sourceCount === targetCount)) {
return Ternary.False;
}
if (source.typeParameters && source.typeParameters !== target.typeParameters) {
target = getCanonicalSignature(target);
source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes);
@@ -10133,19 +10168,10 @@ namespace ts {
}
const paramCount = Math.max(sourceCount, targetCount);
const lastIndex = paramCount - 1;
for (let i = 0; i < paramCount; i++) {
let targetType;
let sourceType = getGenericRestTypeAtPosition(source, i);
if (sourceType) {
targetType = getGenericRestTypeAtPosition(target, i);
if (!targetType) {
return Ternary.False;
}
}
else {
sourceType = getTypeAtPosition(source, i);
targetType = getTypeAtPosition(target, i);
}
const sourceType = i === lastIndex && sourceRestTypeParameter || getTypeAtPosition(source, i);
const targetType = i === lastIndex && targetRestTypeParameter || getTypeAtPosition(target, i);
// In order to ensure that any generic type Foo<T> is at least co-variant with respect to T no matter
// how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions,
// they naturally relate only contra-variantly). However, if the source and target parameters both have
@@ -11921,7 +11947,7 @@ namespace ts {
}
function isTupleLikeType(type: Type): boolean {
return !!getPropertyOfType(type, "0" as __String);
return isTupleType(type) || !!getPropertyOfType(type, "0" as __String);
}
function isNeitherUnitTypeNorNever(type: Type): boolean {
@@ -12312,25 +12338,27 @@ namespace ts {
function forEachMatchingParameterType(source: Signature, target: Signature, callback: (s: Type, t: Type) => void) {
const sourceCount = getParameterCount(source);
for (let i = 0; i < sourceCount; i++) {
const targetRest = getGenericRestTypeAtPosition(target, i);
if (targetRest) {
const sourceRest = getTypeOfRestParameter(source);
if (sourceRest && i === source.parameters.length - 1) {
callback(sourceRest, targetRest);
}
else {
const types: Type[] = [];
for (let j = i; j < sourceCount; j++) {
types.push(getTypeAtPosition(source, j));
}
const rest = !sourceRest || isTupleType(sourceRest) ? createTupleType(types) : createArrayType(getUnionType(types));
callback(rest, targetRest);
}
break;
}
const targetRest = getRestTypeParameter(target);
const paramCount = targetRest ? Math.min(getParameterCount(target) - 1, sourceCount) : sourceCount;
for (let i = 0; i < paramCount; i++) {
callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i));
}
if (targetRest) {
const sourceRest = getRestTypeParameter(source);
if (sourceRest && paramCount === sourceCount - 1) {
callback(sourceRest, targetRest);
}
else {
const types: Type[] = [];
for (let i = paramCount; i < sourceCount; i++) {
types.push(getTypeAtPosition(source, i));
}
const minArgumentCount = getMinArgumentCount(source);
const minLength = minArgumentCount < paramCount ? 0 : minArgumentCount - paramCount;
const rest = hasEffectiveRestParameter(source) ? createArrayType(getUnionType(types)) : createTupleType(types, minLength);
callback(rest, targetRest);
}
}
}
function createInferenceContext(typeParameters: TypeParameter[], signature: Signature | undefined, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext {
@@ -15852,9 +15880,7 @@ namespace ts {
}
}
}
if (elementTypes.length) {
return createTupleType(elementTypes);
}
return createTupleType(elementTypes);
}
}
return createArrayType(elementTypes.length ?
@@ -17823,7 +17849,9 @@ namespace ts {
// We perform two passes over the arguments. In the first pass we infer from all arguments, but use
// wildcards for all context sensitive function expressions.
const argCount = getEffectiveArgumentCount(node, args, signature);
const effectiveArgCount = getEffectiveArgumentCount(node, args, signature);
const restTypeParameter = getRestTypeParameter(signature);
const argCount = restTypeParameter ? Math.min(getParameterCount(signature) - 1, effectiveArgCount) : effectiveArgCount;
for (let i = 0; i < argCount; i++) {
const arg = getEffectiveArgument(node, args, i);
// If the effective argument is 'undefined', then it is an argument that is present but is synthetic.
@@ -17833,25 +17861,20 @@ namespace ts {
// If the effective argument type is 'undefined', there is no synthetic type
// for the argument. In that case, we should check the argument.
if (argType === undefined) {
const restType = getGenericRestTypeAtPosition(signature, i);
if (!restType) {
// For context sensitive arguments we pass the identityMapper, which is a signal to treat all
// context sensitive function expressions as wildcards
const mapper = excludeArgument && excludeArgument[i] !== undefined ? identityMapper : context;
argType = checkExpressionWithContextualType(arg!, paramType, mapper);
}
else {
// The parameter list ends in a rest parameter with a generic type. We consume the remainder
// of the argument list an infer a tuple type or an array type.
argType = inferGenericRestType(signature, args, context);
paramType = restType;
i = argCount;
}
// For context sensitive arguments we pass the identityMapper, which is a signal to treat all
// context sensitive function expressions as wildcards
const mapper = excludeArgument && excludeArgument[i] !== undefined ? identityMapper : context;
argType = checkExpressionWithContextualType(arg!, paramType, mapper);
}
inferTypes(context.inferences, argType, paramType);
}
}
if (restTypeParameter) {
const spreadType = getSpreadArgumentType(node, args, argCount, effectiveArgCount, restTypeParameter, context);
inferTypes(context.inferences, spreadType, restTypeParameter);
}
// In the second pass we visit only context sensitive arguments, and only those that aren't excluded, this
// time treating function expressions normally (which may cause previously inferred type arguments to be fixed
// as we construct types for contextually typed parameters)
@@ -17860,7 +17883,7 @@ namespace ts {
if (excludeArgument) {
for (let i = 0; i < argCount; i++) {
// No need to check for omitted args and template expressions, their exclusion value is always undefined
if (excludeArgument[i] === false && !getGenericRestTypeAtPosition(signature, i)) {
if (excludeArgument[i] === false) {
const arg = args[i];
const paramType = getTypeAtPosition(signature, i);
inferTypes(context.inferences, checkExpressionWithContextualType(arg, paramType, context), paramType);
@@ -17870,23 +17893,26 @@ namespace ts {
return getInferredTypes(context);
}
function inferGenericRestType(signature: Signature, args: ReadonlyArray<Expression>, context: InferenceContext): Type {
const startIndex = signature.parameters.length - 1;
if (args[startIndex].kind === SyntaxKind.SpreadElement && args.length === startIndex + 1) {
// We are inferring from a spread expression in the last argument position, i.e. both the parameter
// and the argument are ...x forms.
return checkExpressionWithContextualType((<SpreadElement>args[startIndex]).expression, getTypeOfRestParameter(signature)!, context);
function getSpreadArgumentType(node: CallLikeExpression, args: ReadonlyArray<Expression>, index: number, argCount: number, restType: TypeParameter, context: InferenceContext | undefined) {
if (index === argCount - 1) {
const arg = getEffectiveArgument(node, args, index);
if (arg && arg.kind === SyntaxKind.SpreadElement) {
// We are inferring from a spread expression in the last argument position, i.e. both the parameter
// and the argument are ...x forms.
return checkExpressionWithContextualType((<SpreadElement>arg).expression, restType, context);
}
}
const contextualType = getTypeAtPosition(signature, startIndex);
const contextualType = getIndexTypeOfType(restType, IndexKind.Number) || anyType;
const types: Type[] = [];
let hasSpreadExpression = false;
for (let i = startIndex; i < args.length; i++) {
const arg = args[i];
types.push(checkExpressionWithContextualType(arg, contextualType, context));
hasSpreadExpression = hasSpreadExpression || arg.kind === SyntaxKind.SpreadElement;
for (let i = index; i < argCount; i++) {
let argType = getEffectiveArgumentType(node, i);
if (!argType) {
argType = checkExpressionWithContextualType(args[i], contextualType, context);
hasSpreadExpression = hasSpreadExpression || args[i].kind === SyntaxKind.SpreadElement;
}
types.push(argType);
}
// If none of the remaining arguments are spread expressions, infer a tuple type. Otherwise, infer
// an array type.
return hasSpreadExpression ? createArrayType(getUnionType(types)) : createTupleType(types);
}
@@ -17977,17 +18003,15 @@ namespace ts {
}
const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1;
const argCount = getEffectiveArgumentCount(node, args, signature);
const restIndex = signature.hasRestParameter ? signature.parameters.length - 1 : -1;
const restType = restIndex >= 0 ? getTypeOfSymbol(signature.parameters[restIndex]) : anyType;
for (let i = 0; i < argCount; i++) {
const arg = getEffectiveArgument(node, args, i);
// If the effective argument is 'undefined', then it is an argument that is present but is synthetic.
if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) {
const restType = getTypeOfRestParameter(signature);
if (arg && arg.kind === SyntaxKind.SpreadElement && i === argCount - 1 && restType &&
signature.parameters.length === argCount && !isArrayType(restType)) {
const argType = checkExpressionWithContextualType((<SpreadElement>arg).expression, restType, /*contextualMapper*/ undefined);
if (!checkTypeRelatedTo(argType, restType, relation, arg, headMessage)) {
return false;
}
if (i === restIndex && (restType.flags & TypeFlags.TypeParameter || arg && arg.kind === SyntaxKind.SpreadElement && !isArrayType(restType))) {
const spreadType = getSpreadArgumentType(node, args, i, argCount, restType, /*context*/ undefined);
return checkTypeRelatedTo(spreadType, restType, relation, arg, headMessage);
}
else {
// Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter)
@@ -18008,7 +18032,6 @@ namespace ts {
}
}
}
return true;
}
@@ -19368,19 +19391,6 @@ namespace ts {
return anyType;
}
function getGenericRestTypeAtPosition(signature: Signature, pos: number) {
if (signature.hasRestParameter) {
const restIndex = signature.parameters.length - 1;
if (pos >= restIndex) {
const restType = getTypeOfSymbol(signature.parameters[restIndex]);
if (restType.flags & TypeFlags.TypeParameter) {
return restType;
}
}
}
return undefined;
}
function getTypeOfRestParameter(signature: Signature) {
return signature.hasRestParameter ? getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]) : undefined;
}
@@ -19399,19 +19409,24 @@ namespace ts {
function getMinArgumentCount(signature: Signature) {
const restType = getTypeOfRestParameter(signature);
if (restType && isTupleType(restType)) {
const elementTypes = (<TypeReference>restType).typeArguments || emptyArray;
let index = elementTypes.length;
while (index > 0) {
--index;
// TODO: Revise this once we have proper support for optional tuple elements
if (!maybeTypeOfKind(elementTypes[index], TypeFlags.Undefined)) {
return signature.parameters.length + index;
}
const minLength = (<TupleType>(<TypeReference>restType).target).minLength;
if (minLength > 0) {
return signature.parameters.length - 1 + minLength;
}
}
return signature.minArgumentCount;
}
function getRestTypeParameter(signature: Signature) {
if (signature.hasRestParameter) {
const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
if (restType.flags & TypeFlags.TypeParameter) {
return <TypeParameter>restType;
}
}
return undefined;
}
function hasEffectiveRestParameter(signature: Signature) {
return signature.hasRestParameter && !isTupleType(getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]));
}
@@ -21665,12 +21680,7 @@ namespace ts {
}
function checkTupleType(node: TupleTypeNode) {
// Grammar checking
const hasErrorFromDisallowedTrailingComma = checkGrammarForDisallowedTrailingComma(node.elementTypes);
if (!hasErrorFromDisallowedTrailingComma && node.elementTypes.length === 0) {
grammarErrorOnNode(node, Diagnostics.A_tuple_type_element_list_cannot_be_empty);
}
checkGrammarForDisallowedTrailingComma(node.elementTypes);
forEach(node.elementTypes, checkSourceElement);
}
@@ -25542,7 +25552,8 @@ namespace ts {
case SyntaxKind.IntersectionType:
return checkUnionOrIntersectionType(<UnionOrIntersectionTypeNode>node);
case SyntaxKind.ParenthesizedType:
return checkSourceElement((<ParenthesizedTypeNode | TypeOperatorNode>node).type);
case SyntaxKind.OptionalType:
return checkSourceElement((<ParenthesizedTypeNode | OptionalTypeNode>node).type);
case SyntaxKind.TypeOperator:
return checkTypeOperator(<TypeOperatorNode>node);
case SyntaxKind.ConditionalType:
-4
View File
@@ -367,10 +367,6 @@
"category": "Error",
"code": 1121
},
"A tuple type element list cannot be empty.": {
"category": "Error",
"code": 1122
},
"Variable declaration list cannot be empty.": {
"category": "Error",
"code": 1123
+7
View File
@@ -640,6 +640,8 @@ namespace ts {
return emitArrayType(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return emitTupleType(<TupleTypeNode>node);
case SyntaxKind.OptionalType:
return emitOptionalType(<OptionalTypeNode>node);
case SyntaxKind.UnionType:
return emitUnionType(<UnionTypeNode>node);
case SyntaxKind.IntersectionType:
@@ -1296,6 +1298,11 @@ namespace ts {
writePunctuation("]");
}
function emitOptionalType(node: OptionalTypeNode) {
emit(node.type);
write("?");
}
function emitUnionType(node: UnionTypeNode) {
emitList(node, node.types, ListFormat.UnionTypeConstituents);
}
+13 -1
View File
@@ -745,12 +745,24 @@ namespace ts {
return node;
}
export function updateTypleTypeNode(node: TupleTypeNode, elementTypes: ReadonlyArray<TypeNode>) {
export function updateTupleTypeNode(node: TupleTypeNode, elementTypes: ReadonlyArray<TypeNode>) {
return node.elementTypes !== elementTypes
? updateNode(createTupleTypeNode(elementTypes), node)
: node;
}
export function createOptionalTypeNode(type: TypeNode) {
const node = createSynthesizedNode(SyntaxKind.OptionalType) as OptionalTypeNode;
node.type = parenthesizeArrayTypeMember(type);
return node;
}
export function updateOptionalTypeNode(node: OptionalTypeNode, type: TypeNode): OptionalTypeNode {
return node.type !== type
? updateNode(createOptionalTypeNode(type), node)
: node;
}
export function createUnionTypeNode(types: ReadonlyArray<TypeNode>): UnionTypeNode {
return <UnionTypeNode>createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, types);
}
+18 -13
View File
@@ -446,19 +446,16 @@ namespace ts {
case SyntaxKind.JsxClosingElement:
return visitNode(cbNode, (<JsxClosingElement>node).tagName);
case SyntaxKind.OptionalType:
case SyntaxKind.JSDocTypeExpression:
return visitNode(cbNode, (<JSDocTypeExpression>node).type);
case SyntaxKind.JSDocNonNullableType:
return visitNode(cbNode, (<JSDocNonNullableType>node).type);
case SyntaxKind.JSDocNullableType:
return visitNode(cbNode, (<JSDocNullableType>node).type);
case SyntaxKind.JSDocOptionalType:
return visitNode(cbNode, (<JSDocOptionalType>node).type);
case SyntaxKind.JSDocVariadicType:
return visitNode(cbNode, (<OptionalTypeNode | JSDocTypeExpression | JSDocTypeReferencingNode>node).type);
case SyntaxKind.JSDocFunctionType:
return visitNodes(cbNode, cbNodes, (<JSDocFunctionType>node).parameters) ||
visitNode(cbNode, (<JSDocFunctionType>node).type);
case SyntaxKind.JSDocVariadicType:
return visitNode(cbNode, (<JSDocVariadicType>node).type);
case SyntaxKind.JSDocComment:
return visitNodes(cbNode, cbNodes, (<JSDoc>node).tags);
case SyntaxKind.JSDocParameterTag:
@@ -2274,7 +2271,7 @@ namespace ts {
function parseJSDocAllType(postFixEquals: boolean): JSDocAllType | JSDocOptionalType {
const result = createNode(SyntaxKind.JSDocAllType) as JSDocAllType;
if (postFixEquals) {
return createJSDocPostfixType(SyntaxKind.JSDocOptionalType, result) as JSDocOptionalType;
return createPostfixType(SyntaxKind.JSDocOptionalType, result) as JSDocOptionalType;
}
else {
nextToken();
@@ -2352,7 +2349,7 @@ namespace ts {
type = finishNode(variadic);
}
if (token() === SyntaxKind.EqualsToken) {
return createJSDocPostfixType(SyntaxKind.JSDocOptionalType, type);
return createPostfixType(SyntaxKind.JSDocOptionalType, type);
}
return type;
}
@@ -2766,9 +2763,17 @@ namespace ts {
return finishNode(node);
}
function parseTupleElementType() {
const type = parseType();
if (!(contextFlags & NodeFlags.JSDoc) && type.kind === SyntaxKind.JSDocNullableType && type.pos === (<JSDocNullableType>type).type.pos) {
type.kind = SyntaxKind.OptionalType;
}
return type;
}
function parseTupleType(): TupleTypeNode {
const node = <TupleTypeNode>createNode(SyntaxKind.TupleType);
node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseTupleElementType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
return finishNode(node);
}
@@ -2960,14 +2965,14 @@ namespace ts {
while (!scanner.hasPrecedingLineBreak()) {
switch (token()) {
case SyntaxKind.ExclamationToken:
type = createJSDocPostfixType(SyntaxKind.JSDocNonNullableType, type);
type = createPostfixType(SyntaxKind.JSDocNonNullableType, type);
break;
case SyntaxKind.QuestionToken:
// If not in JSDoc and next token is start of a type we have a conditional type
if (!(contextFlags & NodeFlags.JSDoc) && lookAhead(nextTokenIsStartOfType)) {
return type;
}
type = createJSDocPostfixType(SyntaxKind.JSDocNullableType, type);
type = createPostfixType(SyntaxKind.JSDocNullableType, type);
break;
case SyntaxKind.OpenBracketToken:
parseExpected(SyntaxKind.OpenBracketToken);
@@ -2992,9 +2997,9 @@ namespace ts {
return type;
}
function createJSDocPostfixType(kind: SyntaxKind, type: TypeNode) {
function createPostfixType(kind: SyntaxKind, type: TypeNode) {
nextToken();
const postfix = createNode(kind, type.pos) as JSDocOptionalType | JSDocNonNullableType | JSDocNullableType;
const postfix = createNode(kind, type.pos) as OptionalTypeNode | JSDocOptionalType | JSDocNonNullableType | JSDocNullableType;
postfix.type = type;
return finishNode(postfix);
}
+1
View File
@@ -380,6 +380,7 @@ namespace ts {
case SyntaxKind.ArrayType:
case SyntaxKind.TupleType:
case SyntaxKind.OptionalType:
case SyntaxKind.TypeLiteral:
case SyntaxKind.TypePredicate:
case SyntaxKind.TypeParameter:
+10
View File
@@ -275,6 +275,7 @@ namespace ts {
TypeLiteral,
ArrayType,
TupleType,
OptionalType,
UnionType,
IntersectionType,
ConditionalType,
@@ -1145,6 +1146,11 @@ namespace ts {
elementTypes: NodeArray<TypeNode>;
}
export interface OptionalTypeNode extends TypeNode {
kind: SyntaxKind.OptionalType;
type: TypeNode;
}
export type UnionOrIntersectionTypeNode = UnionTypeNode | IntersectionTypeNode;
export interface UnionTypeNode extends TypeNode {
@@ -3885,6 +3891,10 @@ namespace ts {
variances?: Variance[]; // Variance of each type parameter
}
export interface TupleType extends GenericType {
minLength: number;
}
export interface UnionOrIntersectionType extends Type {
types: Type[]; // Constituent types
/* @internal */
+5 -1
View File
@@ -372,9 +372,13 @@ namespace ts {
visitNode((<ArrayTypeNode>node).elementType, visitor, isTypeNode));
case SyntaxKind.TupleType:
return updateTypleTypeNode((<TupleTypeNode>node),
return updateTupleTypeNode((<TupleTypeNode>node),
nodesVisitor((<TupleTypeNode>node).elementTypes, visitor, isTypeNode));
case SyntaxKind.OptionalType:
return updateOptionalTypeNode((<OptionalTypeNode>node),
visitNode((<OptionalTypeNode>node).type, visitor, isTypeNode));
case SyntaxKind.UnionType:
return updateUnionTypeNode(<UnionTypeNode>node,
nodesVisitor((<UnionTypeNode>node).types, visitor, isTypeNode));