JS: Add TemplateLiteralTypeExpr

This commit is contained in:
Asger Feldthaus
2020-10-29 21:14:27 +00:00
parent 9da5c5cc70
commit 5676891e44
14 changed files with 210 additions and 7 deletions

View File

@@ -1213,6 +1213,29 @@ class InferTypeExpr extends @infer_typeexpr, TypeParameterized, TypeExpr {
override string getAPrimaryQlClass() { result = "InferTypeExpr" }
}
/**
* A template literal used as a type.
*/
class TemplateLiteralTypeExpr extends @template_literal_typeexpr, TypeExpr {
/**
* Gets the `i`th element of this template literal, which may either
* be a type expression or a constant template element.
*/
ExprOrType getElement(int i) { result = getChild(i) }
/**
* Gets an element of this template literal.
*/
ExprOrType getAnElement() { result = getElement(_) }
/**
* Gets the number of elements of this template literal.
*/
int getNumElement() { result = count(getAnElement()) }
override string getAPrimaryQlClass() { result = "TemplateLiteralTypeExpr" }
}
/**
* A scope induced by a conditional type expression whose `extends` type
* contains `infer` types.

View File

@@ -584,6 +584,7 @@ case @typeexpr.kind of
| 34 = @rest_typeexpr
| 35 = @bigint_literal_typeexpr
| 36 = @readonly_typeexpr
| 37 = @template_literal_typeexpr
;
@typeref = @typeaccess | @type_decl;

View File

@@ -0,0 +1,57 @@
// Based on snippets in https://devblogs.microsoft.com/typescript/announcing-typescript-4-1-beta/
type World = "world";
type Greeting = `hello ${World}`;
type Color = "red" | "blue";
type Quantity = "one" | "two";
type SeussFish = `${Quantity | Color} fish`;
type VerticalAlignment = "top" | "middle" | "bottom";
type HorizontalAlignment = "left" | "center" | "right";
declare function setAlignment(value: `${VerticalAlignment}-${HorizontalAlignment}`): void;
type PropEventSource<T> = {
on<K extends string & keyof T>(eventName: `${K}Changed`, callback: (newValue: T[K]) => void ): void;
};
declare function makeWatchedObject<T>(obj: T): T & PropEventSource<T>;
let person = makeWatchedObject({
firstName: "Homer",
age: 42,
location: "Springfield",
});
// Can no longer be parsed by TypeScript:
// type EnthusiasticGreeting<T extends string> = `${uppercase T}`
type HELLO = EnthusiasticGreeting<"hello">;
// Can no longer be parsed by TypeScript:
// type Getters<T> = {
// [K in keyof T as `get${capitalize K}`]: () => T[K]
// };
interface Person {
name: string;
age: number;
location: string;
}
type LazyPerson = Getters<Person>;
type RemoveKindField<T> = {
[K in keyof T as Exclude<K, "kind">]: T[K]
};
interface Circle {
kind: "circle";
radius: number;
}
type KindlessCircle = RemoveKindField<Circle>;

View File

@@ -0,0 +1,9 @@
| templateLiteralTypes.ts:4:17:4:32 | `hello ${World}` | 0 | templateLiteralTypes.ts:4:18:4:23 | hello |
| templateLiteralTypes.ts:4:17:4:32 | `hello ${World}` | 1 | templateLiteralTypes.ts:4:26:4:30 | World |
| templateLiteralTypes.ts:9:18:9:43 | `${Quan ... } fish` | 0 | templateLiteralTypes.ts:9:21:9:36 | Quantity \| Color |
| templateLiteralTypes.ts:9:18:9:43 | `${Quan ... } fish` | 1 | templateLiteralTypes.ts:9:38:9:42 | fish |
| templateLiteralTypes.ts:14:38:14:82 | `${Vert ... nment}` | 0 | templateLiteralTypes.ts:14:41:14:57 | VerticalAlignment |
| templateLiteralTypes.ts:14:38:14:82 | `${Vert ... nment}` | 1 | templateLiteralTypes.ts:14:59:14:59 | - |
| templateLiteralTypes.ts:14:38:14:82 | `${Vert ... nment}` | 2 | templateLiteralTypes.ts:14:62:14:80 | HorizontalAlignment |
| templateLiteralTypes.ts:18:47:18:59 | `${K}Changed` | 0 | templateLiteralTypes.ts:18:50:18:50 | K |
| templateLiteralTypes.ts:18:47:18:59 | `${K}Changed` | 1 | templateLiteralTypes.ts:18:52:18:58 | Changed |

View File

@@ -0,0 +1,5 @@
import javascript
query ExprOrType getElement(TemplateLiteralTypeExpr e, int i) {
result = e.getElement(i)
}

View File

@@ -3,4 +3,8 @@ import SomeInterface from 'somewhere';
class SomeClass implements SomeInterface {
}
new SomeClass();
new SomeClass();
import SomethingElse from 'somewhere'; // OK: SomethingElse is used in a type
type T = `Now for ${SomethingElse}`;