license import { Node } from './index' ; export type Type = NameType | UnionType | ArrayType | FunctionType | ConstructorType | RecordType | IntersectionType | IndexableObjectType | ParamType | ParameterizedType ; export class NameType { readonly kind = 'name' ; name : string ; constructor ( name ) { this . name = name ; } * traverse ( ) { yield this ; } serialize ( ) { return this . name ; } } export class UnionType { readonly kind = 'union' ; members : Type [ ] ; constructor ( members ) { this . members = members ; } * traverse ( ) { for ( const m of this . members ) { yield * m . traverse ( ) ; } yield this ; } /**
     * Simplify this union type:
     *
     * 1) Flatten nested unions (`foo|(bar|baz)` -> `foo|bar|baz`).
     * 2) De-duplicate identical members (`foo|bar|foo` -> `foo|bar`).
     */ simplify ( ) { const flattened = [ ] ; for ( const m of this . members ) { if ( m . kind === 'union' ) { flattened . push ( ... m . members ) ; } else { flattened . push ( m ) ; } } const deduped = [ ] ; const names = new Set ( ) ; let hasNull = false ; let hasUndefined = false ; for ( const m of flattened ) { if ( m . kind === 'name' ) { if ( m . name === 'null' ) { hasNull = true ; } else if ( m . name === 'undefined' ) { hasUndefined = true ; } else if ( ! names . has ( m . name ) ) { deduped . push ( m ) ; names . add ( m . name ) ; } } else { deduped . push ( m ) ; } } if ( hasNull ) { deduped . push ( nullType ) ; } if ( hasUndefined ) { deduped . push ( undefinedType ) ; } this . members = deduped ; } serialize ( ) { return this . members . map ( ( member ) => { let s = member . serialize ( ) ; if ( member . kind === 'function' ) { s = '(' + s + ')' ; } return s ; } ) . join ( '|' ) ; } } export class ArrayType { readonly kind = 'array' ; itemType : Type ; constructor ( itemType ) { this . itemType = itemType ; } * traverse ( ) { yield * this . itemType . traverse ( ) ; yield this ; } serialize ( ) { if ( this . itemType . kind === 'name' ) { return `${ this . itemType . serialize ( ) }[]` ; } else { return `Array<${ this . itemType . serialize ( ) }>` ; } } } export class ParameterizedType { readonly kind = 'parameterized' ; itemTypes : Type [ ] ; name : string ; constructor ( name , itemTypes ) { this . name = name ; this . itemTypes = itemTypes ; } * traverse ( ) { for ( const itemType of this . itemTypes ) { yield * itemType . traverse ( ) ; } yield this ; } serialize ( ) { const types = this . itemTypes . map ( ( t ) => t . serialize ( ) ) ; return `${ this . name }<${ types . join ( ', ' ) }>` ; } } export class FunctionType { readonly kind = 'function' ; params : ParamType [ ] ; returns : Type ; constructor ( params , returns ) { this . params = params ; this . returns = returns ; } * traverse ( ) { for ( const p of this . params ) { yield * p . traverse ( ) ; } yield * this . returns . traverse ( ) ; yield this ; } serialize ( ) { const params = this . params . map ( ( param ) => param . serialize ( ) ) ; return `(${ params . join ( ', ' ) }) => ${ this . returns . serialize ( ) }` ; } } export class ConstructorType { readonly kind = 'constructor' ; params : ParamType [ ] ; returns : NameType ; constructor ( params , returns ) { this . params = params ; this . returns = returns ; } * traverse ( ) { for ( const p of this . params ) { yield * p . traverse ( ) ; } yield * this . returns . traverse ( ) ; yield this ; } serialize ( ) { const params = this . params . map ( ( param ) => param . serialize ( ) ) ; return `{new(${ params . join ( ', ' ) }): ${ this . returns . serialize ( ) }}` ; } } export class ParamType { readonly kind = 'param' ; name : string ; type : Type ; optional : boolean ; rest : boolean ; description : string ; constructor ( data ) { this . name = data . name ; this . type = data . type || anyType ; this . optional = data . optional || false ; this . rest = data . rest || false ; this . description = data . description || '' ; } * traverse ( ) { yield * this . type . traverse ( ) ; yield this ; } serialize ( ) { let out = '' ; if ( this . rest ) { out += '...' ; } out += this . name ; if ( this . optional ) { out += '?' ; } out += ': ' + this . type . serialize ( ) ; return out ; } } export class RecordType { readonly kind = 'record' ; fields : ParamType [ ] ; constructor ( fields ) { this . fields = fields ; } * traverse ( ) { for ( const f of this . fields ) { yield * f . traverse ( ) ; } yield this ; } serialize ( ) { const fields = this . fields . map ( ( field ) => field . serialize ( ) ) ; return `{${ fields . join ( ', ' ) }}` ; } } export class IntersectionType { readonly kind = 'intersection' ; types : Type [ ] ; constructor ( types ) { this . types = types ; } * traverse ( ) { for ( const t of this . types ) { yield * t . traverse ( ) ; } yield this ; } serialize ( ) { return this . types . map ( ( t ) => t . serialize ( ) ) . join ( ' & ' ) ; } } export class IndexableObjectType { readonly kind = 'indexableObject' ; keyType : Type ; valueType : Type ; constructor ( keyType , valueType ) { this . keyType = keyType ; this . valueType = valueType ; } * traverse ( ) { yield * this . keyType . traverse ( ) ; yield * this . valueType . traverse ( ) ; yield this ; } serialize ( ) { return `{[key: ${ this . keyType . serialize ( ) }]: ${ this . valueType . serialize ( ) }}` ; } } export const anyType = new NameType ( 'any' ) ; export const nullType = new NameType ( 'null' ) ; export const undefinedType = new NameType ( 'undefined' ) ;