Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intrinsic string types #40580

Merged
merged 22 commits into from
Sep 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 139 additions & 53 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3043,6 +3043,10 @@
"category": "Error",
"code": 2794
},
"The 'intrinsic' keyword can only be used to declare compiler provided intrinsic types.": {
"category": "Error",
"code": 2795
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
9 changes: 0 additions & 9 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2023,15 +2023,6 @@ namespace ts {
}

function emitTemplateTypeSpan(node: TemplateLiteralTypeSpan) {
const keyword = node.casing === TemplateCasing.Uppercase ? "uppercase" :
node.casing === TemplateCasing.Lowercase ? "lowercase" :
node.casing === TemplateCasing.Capitalize ? "capitalize" :
node.casing === TemplateCasing.Uncapitalize ? "uncapitalize" :
undefined;
if (keyword) {
writeKeyword(keyword);
writeSpace();
}
emit(node.type);
emit(node.literal);
}
Expand Down
10 changes: 4 additions & 6 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1605,21 +1605,19 @@ namespace ts {
}

// @api
function createTemplateLiteralTypeSpan(casing: TemplateCasing, type: TypeNode, literal: TemplateMiddle | TemplateTail) {
function createTemplateLiteralTypeSpan(type: TypeNode, literal: TemplateMiddle | TemplateTail) {
const node = createBaseNode<TemplateLiteralTypeSpan>(SyntaxKind.TemplateLiteralTypeSpan);
node.casing = casing;
node.type = type;
node.literal = literal;
node.transformFlags = TransformFlags.ContainsTypeScript;
return node;
}

// @api
function updateTemplateLiteralTypeSpan(casing: TemplateCasing, node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddle | TemplateTail) {
return node.casing !== casing
|| node.type !== type
function updateTemplateLiteralTypeSpan(node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddle | TemplateTail) {
return node.type !== type
|| node.literal !== literal
? update(createTemplateLiteralTypeSpan(casing, type, literal), node)
? update(createTemplateLiteralTypeSpan(type, literal), node)
: node;
}

Expand Down
11 changes: 1 addition & 10 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2618,22 +2618,13 @@ namespace ts {
const pos = getNodePos();
return finishNode(
factory.createTemplateLiteralTypeSpan(
parseTemplateCasing(),
parseType(),
parseLiteralOfTemplateSpan(/*isTaggedTemplate*/ false)
),
pos
);
}

function parseTemplateCasing(): TemplateCasing {
return parseOptional(SyntaxKind.UppercaseKeyword) ? TemplateCasing.Uppercase :
parseOptional(SyntaxKind.LowercaseKeyword) ? TemplateCasing.Lowercase :
parseOptional(SyntaxKind.CapitalizeKeyword) ? TemplateCasing.Capitalize :
parseOptional(SyntaxKind.UncapitalizeKeyword) ? TemplateCasing.Uncapitalize :
TemplateCasing.None;
}

function parseLiteralOfTemplateSpan(isTaggedTemplate: boolean) {
if (token() === SyntaxKind.CloseBraceToken) {
reScanTemplateToken(isTaggedTemplate);
Expand Down Expand Up @@ -6751,7 +6742,7 @@ namespace ts {
const name = parseIdentifier();
const typeParameters = parseTypeParameters();
parseExpected(SyntaxKind.EqualsToken);
const type = parseType();
const type = token() === SyntaxKind.IntrinsicKeyword && tryParse(parseKeywordAndNoDot) || parseType();
parseSemicolon();
const node = factory.createTypeAliasDeclaration(decorators, modifiers, name, typeParameters, type);
return withJSDoc(finishNode(node, pos), hasJSDoc);
Expand Down
5 changes: 1 addition & 4 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ namespace ts {
infer: SyntaxKind.InferKeyword,
instanceof: SyntaxKind.InstanceOfKeyword,
interface: SyntaxKind.InterfaceKeyword,
intrinsic: SyntaxKind.IntrinsicKeyword,
is: SyntaxKind.IsKeyword,
keyof: SyntaxKind.KeyOfKeyword,
let: SyntaxKind.LetKeyword,
Expand Down Expand Up @@ -151,10 +152,6 @@ namespace ts {
yield: SyntaxKind.YieldKeyword,
async: SyntaxKind.AsyncKeyword,
await: SyntaxKind.AwaitKeyword,
uppercase: SyntaxKind.UppercaseKeyword,
lowercase: SyntaxKind.LowercaseKeyword,
capitalize: SyntaxKind.CapitalizeKeyword,
uncapitalize: SyntaxKind.UncapitalizeKeyword,
of: SyntaxKind.OfKeyword,
};

Expand Down
39 changes: 15 additions & 24 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ namespace ts {
DeclareKeyword,
GetKeyword,
InferKeyword,
IntrinsicKeyword,
IsKeyword,
KeyOfKeyword,
ModuleKeyword,
Expand All @@ -186,10 +187,6 @@ namespace ts {
FromKeyword,
GlobalKeyword,
BigIntKeyword,
UppercaseKeyword,
LowercaseKeyword,
CapitalizeKeyword,
UncapitalizeKeyword,
OfKeyword, // LastKeyword and LastToken and LastContextualKeyword

// Parse tree nodes
Expand Down Expand Up @@ -544,7 +541,6 @@ namespace ts {
| SyntaxKind.BigIntKeyword
| SyntaxKind.BooleanKeyword
| SyntaxKind.BreakKeyword
| SyntaxKind.CapitalizeKeyword
| SyntaxKind.CaseKeyword
| SyntaxKind.CatchKeyword
| SyntaxKind.ClassKeyword
Expand Down Expand Up @@ -574,10 +570,10 @@ namespace ts {
| SyntaxKind.InKeyword
| SyntaxKind.InstanceOfKeyword
| SyntaxKind.InterfaceKeyword
| SyntaxKind.IntrinsicKeyword
| SyntaxKind.IsKeyword
| SyntaxKind.KeyOfKeyword
| SyntaxKind.LetKeyword
| SyntaxKind.LowercaseKeyword
| SyntaxKind.ModuleKeyword
| SyntaxKind.NamespaceKeyword
| SyntaxKind.NeverKeyword
Expand Down Expand Up @@ -605,11 +601,9 @@ namespace ts {
| SyntaxKind.TryKeyword
| SyntaxKind.TypeKeyword
| SyntaxKind.TypeOfKeyword
| SyntaxKind.UncapitalizeKeyword
| SyntaxKind.UndefinedKeyword
| SyntaxKind.UniqueKeyword
| SyntaxKind.UnknownKeyword
| SyntaxKind.UppercaseKeyword
| SyntaxKind.VarKeyword
| SyntaxKind.VoidKeyword
| SyntaxKind.WhileKeyword
Expand All @@ -635,6 +629,7 @@ namespace ts {
| SyntaxKind.AnyKeyword
| SyntaxKind.BigIntKeyword
| SyntaxKind.BooleanKeyword
| SyntaxKind.IntrinsicKeyword
| SyntaxKind.NeverKeyword
| SyntaxKind.NumberKeyword
| SyntaxKind.ObjectKeyword
Expand Down Expand Up @@ -1665,19 +1660,10 @@ namespace ts {
export interface TemplateLiteralTypeSpan extends TypeNode {
readonly kind: SyntaxKind.TemplateLiteralTypeSpan,
readonly parent: TemplateLiteralTypeNode;
readonly casing: TemplateCasing;
readonly type: TypeNode;
readonly literal: TemplateMiddle | TemplateTail;
}

export const enum TemplateCasing {
None,
Uppercase,
Lowercase,
Capitalize,
Uncapitalize,
}

// Note: 'brands' in our syntax nodes serve to give us a small amount of nominal typing.
// Consider 'Expression'. Without the brand, 'Expression' is actually no different
// (structurally) than 'Node'. Because of this you can pass any Node to a function that
Expand Down Expand Up @@ -4892,6 +4878,7 @@ namespace ts {
Substitution = 1 << 25, // Type parameter substitution
NonPrimitive = 1 << 26, // intrinsic object type
TemplateLiteral = 1 << 27, // Template literal type
StringMapping = 1 << 28, // Uppercase/Lowercase type

/* @internal */
AnyOrUnknown = Any | Unknown,
Expand All @@ -4909,7 +4896,7 @@ namespace ts {
Intrinsic = Any | Unknown | String | Number | BigInt | Boolean | BooleanLiteral | ESSymbol | Void | Undefined | Null | Never | NonPrimitive,
/* @internal */
Primitive = String | Number | BigInt | Boolean | Enum | EnumLiteral | ESSymbol | Void | Undefined | Null | Literal | UniqueESSymbol,
StringLike = String | StringLiteral | TemplateLiteral,
StringLike = String | StringLiteral | TemplateLiteral | StringMapping,
NumberLike = Number | NumberLiteral | Enum,
BigIntLike = BigInt | BigIntLiteral,
BooleanLike = Boolean | BooleanLiteral,
Expand All @@ -4922,15 +4909,15 @@ namespace ts {
StructuredType = Object | Union | Intersection,
TypeVariable = TypeParameter | IndexedAccess,
InstantiableNonPrimitive = TypeVariable | Conditional | Substitution,
InstantiablePrimitive = Index | TemplateLiteral,
InstantiablePrimitive = Index | TemplateLiteral | StringMapping,
Instantiable = InstantiableNonPrimitive | InstantiablePrimitive,
StructuredOrInstantiable = StructuredType | Instantiable,
/* @internal */
ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection,
/* @internal */
Simplifiable = IndexedAccess | Conditional,
/* @internal */
Substructure = Object | Union | Intersection | Index | IndexedAccess | Conditional | Substitution | TemplateLiteral,
Substructure = Object | Union | Intersection | Index | IndexedAccess | Conditional | Substitution | TemplateLiteral | StringMapping,
// 'Narrowable' types are types where narrowing actually narrows.
// This *should* be every type other than null, undefined, void, and never
Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive,
Expand Down Expand Up @@ -5379,11 +5366,15 @@ namespace ts {
}

export interface TemplateLiteralType extends InstantiableType {
texts: readonly string[]; // Always one element longer than casings/types
casings: readonly TemplateCasing[]; // Always at least one element
texts: readonly string[]; // Always one element longer than types
types: readonly Type[]; // Always at least one element
}

export interface StringMappingType extends InstantiableType {
symbol: Symbol;
type: Type;
}

// Type parameter substitution (TypeFlags.Substitution)
// Substitution types are created for type parameters or indexed access types that occur in the
// true branch of a conditional type. For example, in 'T extends string ? Foo<T> : Bar<T>', the
Expand Down Expand Up @@ -6774,8 +6765,8 @@ namespace ts {
createIndexSignature(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration;
/* @internal */ createIndexSignature(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined): IndexSignatureDeclaration; // eslint-disable-line @typescript-eslint/unified-signatures
updateIndexSignature(node: IndexSignatureDeclaration, decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration;
createTemplateLiteralTypeSpan(casing: TemplateCasing, type: TypeNode, literal: TemplateMiddle | TemplateTail): TemplateLiteralTypeSpan;
updateTemplateLiteralTypeSpan(casing: TemplateCasing, node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddle | TemplateTail): TemplateLiteralTypeSpan;
createTemplateLiteralTypeSpan(type: TypeNode, literal: TemplateMiddle | TemplateTail): TemplateLiteralTypeSpan;
updateTemplateLiteralTypeSpan(node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddle | TemplateTail): TemplateLiteralTypeSpan;

//
// Types
Expand Down
4 changes: 4 additions & 0 deletions src/harness/fourslashInterfaceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,10 @@ namespace FourSlashInterface {
typeEntry("ConstructorParameters"),
typeEntry("ReturnType"),
typeEntry("InstanceType"),
typeEntry("Uppercase"),
typeEntry("Lowercase"),
typeEntry("Capitalize"),
typeEntry("Uncapitalize"),
interfaceEntry("ThisType"),
varEntry("ArrayBuffer"),
interfaceEntry("ArrayBufferTypes"),
Expand Down
20 changes: 20 additions & 0 deletions src/lib/es5.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,26 @@ type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => i
*/
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;

/**
* Convert string literal type to uppercase
*/
type Uppercase<S extends string> = intrinsic;

/**
* Convert string literal type to lowercase
*/
type Lowercase<S extends string> = intrinsic;

/**
* Convert first character of string literal type to uppercase
*/
type Capitalize<S extends string> = intrinsic;

/**
* Convert first character of string literal type to lowercase
*/
type Uncapitalize<S extends string> = intrinsic;

/**
* Marker for contextual 'this' type
*/
Expand Down
Loading