Skip to content

Instantly share code, notes, and snippets.

@rbuckton
Last active July 28, 2021 00:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rbuckton/f6ee6fcdcc21d44fdfa0 to your computer and use it in GitHub Desktop.
Save rbuckton/f6ee6fcdcc21d44fdfa0 to your computer and use it in GitHub Desktop.
/**
* Basic shape for a type.
*/
interface Type {
/**
* Describes the specific shape of the type.
* @remarks
* One of:
* "any" -> IntrinsicType
* "number" -> IntrinsicType
* "boolean" -> IntrinsicType
* "string" -> IntrinsicType
* "symbol" -> IntrinsicType
* "void" -> IntrinsicType
* "parameter" -> TypeParameter
* "reference" -> TypeReference
* "predicate" -> TypePredicate
* "array" -> ArrayType
* "interface" -> InterfaceType
* "class" -> ClassType
* "tuple" -> TupleType
* "union" -> UnionType
* "intersection" -> IntersectionType
* "function" -> FunctionType
*/
kind: string;
}
/**
* An intrinsic type.
*/
interface IntrinsicType extends Type {
kind: string; // "any", "number", "boolean", "string", "symbol", or "void"
}
/**
* A generic type parameter.
*/
interface TypeParameter extends Type {
kind: string; // "parameter"
/**
* The name of the type parameter. Optional, may be undefined.
*/
name?: string;
/**
* An optional constraint for the type parameter.
*/
constraint?: Type;
}
/**
* A reference to a generic type.
*/
interface TypeReference extends Type {
kind: string; // "reference"
/**
* The referenced generic type
*/
type: Type;
/**
* The generic type arguments, in order.
*/
typeArguments?: Type[];
}
interface TypePredicate extends Type {
kind: string; // "predicate"
/**
* The ordinal offset of the parameter in the parameter list
*/
parameterIndex: number;
/**
* The type for the type predicate.
*/
type: Type;
}
interface ArrayType extends Type {
kind: string; // "array"
/**
* The element type for the array.
*/
elementType: Type;
}
/**
* Describes an interface.
*/
interface InterfaceType extends Type {
kind: string; // "interface"
/**
* The name of the interface. Optional, may be undefined.
*/
name?: string;
/**
* Generic type parameters for the type. May be undefined.
*/
typeParameters?: TypeParameter[];
/**
* Implemented interfaces.
*/
implements?: Type[];
/**
* Members for the type. May be undefined.
* @remarks Contains property, accessor, and method declarations.
*/
members?: {
[key: string]: Type;
[key: number]: Type;
};
/**
* Call signatures for the type. May be undefined.
*/
call?: Signature[];
/**
* Construct signatures for the type. May be undefined.
*/
construct?: Signature[];
/**
* Index signatures for the type. May be undefined.
*/
index?: Signature[];
}
/**
* Describes a class.
*/
interface ClassType extends Type {
kind: string; // "class"
/**
* The name of the class. Optional, may be undefined.
*/
name?: string;
/**
* Generic type parameters for the type. May be undefined.
*/
typeParameters?: TypeParameter[];
/**
* The superclass for the type.
*/
extends?: Type;
/**
* Implemented interfaces.
*/
implements?: Type[];
/**
* Members for the type. May be undefined.
* @remarks Contains property, accessor, and method declarations.
*/
members?: {
[key: string]: Type;
[key: number]: Type;
// [key: symbol]: Type;
};
/**
* Static members for the type. May be undefined.
* @remarks Contains property, accessor, and method declarations.
*/
statics?: {
[key: string]: Type;
[key: number]: Type;
// [key: symbol]: Type;
};
/**
* Call signatures for the type. May be undefined.
*/
call?: Signature[];
/**
* Construct signatures for the type. May be undefined.
*/
construct?: Signature[];
/**
* Index signatures for the type. May be undefined.
*/
index?: Signature[];
/**
* The constructor function for the class.
*/
getConstructor?(): Function;
}
/**
* Describes a tuple type.
*/
interface TupleType extends Type {
kind: string; // "tuple"
/**
* Types of each element in the tuple.
*/
elements: Type[];
}
/**
* Describes a union type.
*/
interface UnionType extends Type {
kind: string; // "union"
/**
* The constituent types of the union.
*/
types: Type[];
}
/**
* Describes an intersection type.
*/
interface IntersectionType extends Type {
kind: string; // "intersection"
/**
* The constituent types of the intersection.
*/
types: Type[];
}
/**
* Describes a function type.
*/
interface FunctionType extends Type {
kind: string; // "function"
/**
* The name of the function. Optional, may be undefined.
*/
name?: string;
/**
* The signatures for the function type
*/
signatures: Signature[];
}
/**
* Describes a parameter.
*/
interface ParameterInfo {
/**
* The name for the parameter. May be undefined.
*/
name?: string;
/**
* The type of the parameter.
*/
type: Type;
}
/**
* Describes a signature.
*/
interface Signature {
/**
* A value indicating whether this is a constructor signature.
*/
construct?: boolean;
/**
* Generic type parameters for the function type. May be undefined.
*/
typeParameters?: TypeParameter[];
/**
* Parameters for the function type.
*/
parameters: ParameterInfo[];
/**
* The number of required parameters of the function type.
*/
length: number;
/**
* A value indicating whether the final argument is a rest
* argument. May be undefined.
*/
rest?: boolean;
/**
* The return type of the function type. May be undefined.
*/
returns?: Type;
}
@pcan
Copy link

pcan commented Aug 28, 2015

@rbuckton I'm working on a type metadata emitter that uses the TypeChecker API to retrieve all type information from a TypeScript project. Today I'm facing circular references, and I did not find a way to describe the following structure using the model you proposed:

class MyClass {

    numberField: number;

    static staticField = class InnerClass {
        ...
    };

    anotherField = class InnerClass2 {
        ...
    }

}

since both staticField and anotherField may be assignable to "values" that are actual subclasses of InnerClass and InnerClass2 respectively, how should I describe the metadata of this class members?
Regarding numberField, this is simply <IntrinsicType>{kind: 'number'}, but instead the serialized type of staticField and anotherField should be the serialized version of the ClassType interface: a metadata on metadata (really weird thing!), but actually I think this model just needs another subclass of Type, something like TypeExpression... Do you have any suggestion about this?

EDIT:

Maybe the following interface could help defining the typeof expressions:

/**
  * Describes a class expression Type (typeof expression)
  */
interface ExpressionType extends Type {
    kind: string; // "typeof"

    /**
      * The target class type referenced by this expression.
      */
    type: ClassType;
}

So, this code:

class Person {
    mate: Person;
    mateType: typeof Person;
}

class Student extends Person {
    constructor(){
        super();
        this.mateType = Student;
    }
}

var s = new Student();
var mate = new s.mateType();

would emit the following metadata:

types.Person = <ClassType>{kind: 'class', name: 'Person'};
types.Student = <ClassType>{kind: 'class', name: 'Student', extends: types.Person};

types.Student.members = {
    mate: types.Person,
    mateType: <ExpressionType>{
        kind: 'typeof',
        type: types.Student
    }
}

Edit 2

I did it. Here (view it in raw mode) there is an example of the output; some details have to be refined, but I think it works (all TypeScript, Lodash & ES6 types have been emitted in the example).

@pleerock
Copy link

pleerock commented Apr 3, 2017

kind shall be a string literal type

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment