Skip to content

Commit

Permalink
type-level non-null operator (!)
Browse files Browse the repository at this point in the history
  • Loading branch information
KiaraGrouwstra committed Aug 25, 2017
1 parent a136f55 commit dc4e63f
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 24 deletions.
9 changes: 7 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7540,6 +7540,10 @@ namespace ts {
return links.resolvedType;
}

function getTypeFromPostfixedTypeOperatorNode(node: NonNullTypeNode) { // TypeOperatorNode
return getNonNullableType(getTypeFromTypeNode(node.type));
}

function createIndexedAccessType(objectType: Type, indexType: Type) {
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
type.objectType = objectType;
Expand Down Expand Up @@ -7985,7 +7989,6 @@ namespace ts {
case SyntaxKind.JSDocNullableType:
return getTypeFromJSDocNullableTypeNode(<JSDocNullableType>node);
case SyntaxKind.ParenthesizedType:
case SyntaxKind.JSDocNonNullableType:
case SyntaxKind.JSDocOptionalType:
case SyntaxKind.JSDocTypeExpression:
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression>node).type);
Expand All @@ -7997,6 +8000,8 @@ namespace ts {
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
case SyntaxKind.TypeOperator:
return getTypeFromTypeOperatorNode(<TypeOperatorNode>node);
case SyntaxKind.NonNullTypeNode:
return getTypeFromPostfixedTypeOperatorNode(<NonNullTypeNode>node);
case SyntaxKind.IndexedAccessType:
return getTypeFromIndexedAccessTypeNode(<IndexedAccessTypeNode>node);
case SyntaxKind.MappedType:
Expand Down Expand Up @@ -18701,6 +18706,7 @@ namespace ts {
}

function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) {
// if (allowSyntheticDefaultImports) console.trace("checkTypeReferenceNode");
checkGrammarTypeArguments(node, node.typeArguments);
if (node.kind === SyntaxKind.TypeReference && node.typeName.jsdocDotPos !== undefined && !isInJavaScriptFile(node) && !isInJSDoc(node)) {
grammarErrorAtPos(node, node.typeName.jsdocDotPos, 1, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments);
Expand Down Expand Up @@ -22310,7 +22316,6 @@ namespace ts {
checkSignatureDeclaration(node as JSDocFunctionType);
// falls through
case SyntaxKind.JSDocVariadicType:
case SyntaxKind.JSDocNonNullableType:
case SyntaxKind.JSDocNullableType:
case SyntaxKind.JSDocAllType:
case SyntaxKind.JSDocUnknownType:
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,8 @@ namespace ts {
return emitAsExpression(<AsExpression>node);
case SyntaxKind.NonNullExpression:
return emitNonNullExpression(<NonNullExpression>node);
case SyntaxKind.NonNullTypeNode:
return emitWithSuffix((<NonNullTypeNode>node).type, "!");
case SyntaxKind.MetaProperty:
return emitMetaProperty(<MetaProperty>node);

Expand Down
12 changes: 12 additions & 0 deletions src/compiler/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1261,6 +1261,18 @@ namespace ts {
: node;
}

export function createNonNullTypeNode(type: TypeNode) {
const node = <NonNullTypeNode>createSynthesizedNode(SyntaxKind.NonNullTypeNode);
node.type = parenthesizeElementTypeMember(type);
return node;
}

export function updateNonNullTypeNode(node: NonNullTypeNode, type: TypeNode) {
return node.type !== type
? updateNode(createNonNullTypeNode(type), node)
: node;
}

export function createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier) {
const node = <MetaProperty>createSynthesizedNode(SyntaxKind.MetaProperty);
node.keywordToken = keywordToken;
Expand Down
45 changes: 35 additions & 10 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ namespace ts {
visitNode(cbNode, (<AsExpression>node).type);
case SyntaxKind.NonNullExpression:
return visitNode(cbNode, (<NonNullExpression>node).expression);
case SyntaxKind.NonNullTypeNode:
return visitNode(cbNode, (<NonNullTypeNode>node).type);
case SyntaxKind.MetaProperty:
return visitNode(cbNode, (<MetaProperty>node).name);
case SyntaxKind.ConditionalExpression:
Expand Down Expand Up @@ -396,8 +398,6 @@ namespace ts {

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:
Expand Down Expand Up @@ -2175,8 +2175,8 @@ namespace ts {
return finishNode(parameter);
}

function parseJSDocNodeWithType(kind: SyntaxKind.JSDocVariadicType | SyntaxKind.JSDocNonNullableType): TypeNode {
const result = createNode(kind) as JSDocVariadicType | JSDocNonNullableType;
function parseJSDocNodeWithType(kind: SyntaxKind.JSDocVariadicType): TypeNode {
const result = createNode(kind) as JSDocVariadicType;
nextToken();
result.type = parseType();
return finishNode(result);
Expand Down Expand Up @@ -2662,8 +2662,6 @@ namespace ts {
return parseJSDocFunctionType();
case SyntaxKind.DotDotDotToken:
return parseJSDocNodeWithType(SyntaxKind.JSDocVariadicType);
case SyntaxKind.ExclamationToken:
return parseJSDocNodeWithType(SyntaxKind.JSDocNonNullableType);
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.TrueKeyword:
Expand Down Expand Up @@ -2744,7 +2742,7 @@ namespace ts {
if (!kind) return type;
nextToken();

const postfix = createNode(kind, type.pos) as JSDocOptionalType | JSDocNonNullableType | JSDocNullableType;
const postfix = createNode(kind, type.pos) as JSDocOptionalType | JSDocNullableType;
postfix.type = type;
return finishNode(postfix);

Expand All @@ -2753,8 +2751,6 @@ namespace ts {
case SyntaxKind.EqualsToken:
// only parse postfix = inside jsdoc, because it's ambiguous elsewhere
return contextFlags & NodeFlags.JSDoc ? SyntaxKind.JSDocOptionalType : undefined;
case SyntaxKind.ExclamationToken:
return SyntaxKind.JSDocNonNullableType;
case SyntaxKind.QuestionToken:
return SyntaxKind.JSDocNullableType;
}
Expand Down Expand Up @@ -2794,7 +2790,36 @@ namespace ts {
case SyntaxKind.KeyOfKeyword:
return parseTypeOperator(SyntaxKind.KeyOfKeyword);
}
return parseArrayTypeOrHigher();
const type = parseArrayTypeOrHigher();
return parsePostfixTypeOperatorOrHigher(type);
}

function parsePostfixTypeOperator(type: TypeNode) { // , parseKind: SyntaxKind, nodeKind: SyntaxKind
const node = <NonNullTypeNode>createNode(SyntaxKind.NonNullTypeNode);
// const node = <NonNullTypeNode>createNode(nodeKind);
// parseExpected(operator);
parseExpected(SyntaxKind.ExclamationToken);
// parseExpected(parseKind);
// node.operator = operator;
node.type = type;
const finished = finishNode(node);
finished.pos = type.pos;
// finished.end = type.end + 1;
return finished;
}

function parsePostfixTypeOperatorOrHigher(type: TypeNode): TypeNode {
let postfixed: TypeNode;
switch (token()) {
case SyntaxKind.ExclamationToken:
postfixed = parsePostfixTypeOperator(type); // , SyntaxKind.ExclamationToken, SyntaxKind.NonNullTypeNode
}
if (postfixed) {
return parsePostfixTypeOperatorOrHigher(postfixed);
}
else {
return type;
}
}

function parseUnionOrIntersectionType(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, parseConstituentType: () => TypeNode, operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken): TypeNode {
Expand Down
14 changes: 7 additions & 7 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ namespace ts {
ExpressionWithTypeArguments,
AsExpression,
NonNullExpression,
NonNullTypeNode,
MetaProperty,

// Misc
Expand Down Expand Up @@ -357,7 +358,6 @@ namespace ts {
// The ? type
JSDocUnknownType,
JSDocNullableType,
JSDocNonNullableType,
JSDocOptionalType,
JSDocFunctionType,
JSDocVariadicType,
Expand Down Expand Up @@ -1545,6 +1545,11 @@ namespace ts {
expression: Expression;
}

export interface NonNullTypeNode extends TypeNode {
kind: SyntaxKind.NonNullTypeNode;
type: TypeNode;
}

// NOTE: MetaProperty is really a MemberExpression, but we consider it a PrimaryExpression
// for the same reasons we treat NewExpression as a PrimaryExpression.
export interface MetaProperty extends PrimaryExpression {
Expand Down Expand Up @@ -2055,11 +2060,6 @@ namespace ts {
kind: SyntaxKind.JSDocUnknownType;
}

export interface JSDocNonNullableType extends JSDocType {
kind: SyntaxKind.JSDocNonNullableType;
type: TypeNode;
}

export interface JSDocNullableType extends JSDocType {
kind: SyntaxKind.JSDocNullableType;
type: TypeNode;
Expand All @@ -2079,7 +2079,7 @@ namespace ts {
type: TypeNode;
}

export type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType;
export type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType;

export interface JSDoc extends Node {
kind: SyntaxKind.JSDocComment;
Expand Down
4 changes: 0 additions & 4 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4539,10 +4539,6 @@ namespace ts {
return node.kind === SyntaxKind.JSDocNullableType;
}

export function isJSDocNonNullableType(node: Node): node is JSDocNonNullableType {
return node.kind === SyntaxKind.JSDocNonNullableType;
}

export function isJSDocOptionalType(node: Node): node is JSDocOptionalType {
return node.kind === SyntaxKind.JSDocOptionalType;
}
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,10 @@ namespace ts {
return updateNonNullExpression(<NonNullExpression>node,
visitNode((<NonNullExpression>node).expression, visitor, isExpression));

case SyntaxKind.NonNullTypeNode:
return updateNonNullTypeNode(<NonNullTypeNode>node,
visitNode((<NonNullTypeNode>node).type, visitor, isTypeNode));

case SyntaxKind.MetaProperty:
return updateMetaProperty(<MetaProperty>node,
visitNode((<MetaProperty>node).name, visitor, isIdentifier));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"kind": "JSDocNonNullableType",
"kind": "NonNullTypeNode",
"pos": 1,
"end": 8,
"flags": "JSDoc",
Expand Down
9 changes: 9 additions & 0 deletions tests/baselines/reference/nonNullType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//// [nonNullType.ts]
type z = string | undefined | null | never;
type a = string | undefined | null | never;
type b = a!;
type Assert<T> = T!;
type c = Assert<a>;


//// [nonNullType.js]
21 changes: 21 additions & 0 deletions tests/baselines/reference/nonNullType.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
=== tests/cases/compiler/nonNullType.ts ===
type z = string | undefined | null | never;
>z : Symbol(z, Decl(nonNullType.ts, 0, 0))

type a = string | undefined | null | never;
>a : Symbol(a, Decl(nonNullType.ts, 0, 43))

type b = a!;
>b : Symbol(b, Decl(nonNullType.ts, 1, 43))
>a : Symbol(a, Decl(nonNullType.ts, 0, 43))

type Assert<T> = T!;
>Assert : Symbol(Assert, Decl(nonNullType.ts, 2, 12))
>T : Symbol(T, Decl(nonNullType.ts, 3, 12))
>T : Symbol(T, Decl(nonNullType.ts, 3, 12))

type c = Assert<a>;
>c : Symbol(c, Decl(nonNullType.ts, 3, 20))
>Assert : Symbol(Assert, Decl(nonNullType.ts, 2, 12))
>a : Symbol(a, Decl(nonNullType.ts, 0, 43))

23 changes: 23 additions & 0 deletions tests/baselines/reference/nonNullType.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
=== tests/cases/compiler/nonNullType.ts ===
type z = string | undefined | null | never;
>z : a
>null : null

type a = string | undefined | null | never;
>a : a
>null : null

type b = a!;
>b : string
>a : a

type Assert<T> = T!;
>Assert : T
>T : T
>T : T

type c = Assert<a>;
>c : a
>Assert : T
>a : a

6 changes: 6 additions & 0 deletions tests/cases/compiler/nonNullType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @strictNullChecks: true
type z = string | undefined | null | never;
type a = string | undefined | null | never;
type b = a!;
type Assert<T> = T!;
type c = Assert<a>;

0 comments on commit dc4e63f

Please sign in to comment.