Add ability to filter out duplicate and generic parameter types

This should make it easier to generate interfaces and use the type
references.
This commit is contained in:
Jerko Steiner 2019-08-12 12:31:13 +07:00
parent 3937778e06
commit aa78b12e54

View File

@ -1,6 +1,14 @@
import * as ts from 'typescript' import * as ts from 'typescript'
import * as fs from 'fs' import * as fs from 'fs'
function isObjectType(type: ts.Type): type is ts.ObjectType {
return !!(type.flags & ts.TypeFlags.Object)
}
function isTypeReference(type: ts.ObjectType): type is ts.TypeReference {
return !!(type.objectFlags & ts.ObjectFlags.Reference)
}
export function typecheck() { export function typecheck() {
interface DocEntry { interface DocEntry {
name?: string name?: string
@ -32,22 +40,51 @@ export function typecheck() {
} }
return return
function typeToString(type: ts.Type): string {
return checker.typeToString(type)
}
function filterGlobalTypes(type: ts.Type): boolean {
const symbol = type.getSymbol()
if (symbol && !((symbol as any).parent)) {
// e.g. Array symbol has no parent
return false
}
if (type.isUnionOrIntersection()) {
// union type params should have already been extracted
return false
}
return true
}
function mapGenericTypes(type: ts.Type): ts.Type {
if (isObjectType(type) && isTypeReference(type)) {
return type.target
}
return type
}
function filterDuplicates(type: ts.Type, i: number, arr: ts.Type[]) {
// TODO improve performance of this method
return i === arr.indexOf(type)
}
function getAllTypeParameters(type: ts.Type): ts.Type[] { function getAllTypeParameters(type: ts.Type): ts.Type[] {
console.log('TTT', checker.typeToString(type), // console.log('TTT', checker.typeToString(type),
{ // {
isObject: !!(type.flags & ts.TypeFlags.Object), // isObject: !!(type.flags & ts.TypeFlags.Object),
isTuple: (type as any).objectFlags & ts.ObjectFlags.Tuple, // isTuple: (type as any).objectFlags & ts.ObjectFlags.Tuple,
objectFlags: (type as any).objectFlags, // typeFlags: type.flags,
}, // objectFlags: (type as any).objectFlags,
) // },
if (type.flags & ts.TypeFlags.Object) { // )
const objectType = type as ts.ObjectType if (isObjectType(type)) {
const objectFlags = objectType.objectFlags if (isTypeReference(type)) {
if (objectFlags & ts.ObjectFlags.Reference) { const types: ts.Type[] = [type]
const types = [type]
const typeRef = type as ts.TypeReference if (type.typeArguments) {
if (typeRef.typeArguments) { type.typeArguments.forEach(t => {
typeRef.typeArguments.forEach(t => {
const ta = getAllTypeParameters(t) const ta = getAllTypeParameters(t)
types.push(...ta) types.push(...ta)
}) })
@ -104,9 +141,11 @@ export function typecheck() {
if (ts.isClassDeclaration(node) && node.name) { if (ts.isClassDeclaration(node) && node.name) {
// This is a top level class, get its symbol // This is a top level class, get its symbol
const symbol = checker.getSymbolAtLocation(node.name) const symbol = checker.getSymbolAtLocation(node.name)
const typeParameters: ts.Type[] = []
if (symbol) { if (symbol) {
console.log('===') console.log('===')
console.log('text', node.getText(node.getSourceFile())) // console.log('text', node.getText(node.getSourceFile()))
console.log('class', symbol.getName()) console.log('class', symbol.getName())
const type = checker.getDeclaredTypeOfSymbol(symbol) const type = checker.getDeclaredTypeOfSymbol(symbol)
@ -115,13 +154,17 @@ export function typecheck() {
console.log(' tp.symbol.name', tp.symbol.name) console.log(' tp.symbol.name', tp.symbol.name)
const constraint = tp.getConstraint() const constraint = tp.getConstraint()
if (constraint) { if (constraint) {
// TODO call getAllTypeParameters here...
console.log(' tp.constraint', console.log(' tp.constraint',
checker.typeToString(constraint)) checker.typeToString(constraint))
} }
const def = tp.getDefault() const def = tp.getDefault()
if (def) { if (def) {
// TODO call getAllTypeParameters here...
console.log(' tp.default', checker.typeToString(def)) console.log(' tp.default', checker.typeToString(def))
} }
typeParameters.push(tp)
}) })
} }
@ -143,31 +186,19 @@ export function typecheck() {
const propType = checker const propType = checker
.getTypeOfSymbolAtLocation(p, p.valueDeclaration) .getTypeOfSymbolAtLocation(p, p.valueDeclaration)
const s = propType.getSymbol() const typeParams = getAllTypeParameters(propType)
if (s) {
// if (ts,
// console.log(' ', p.getName(), checker.getFullyQualifiedName(s))
// s.value
// checker.
// const d = s.getDeclarations()
// if (d) {
// d.forEach(dec => {
// console.log(' ', dec.getText())
// })
// }
}
p.flags
console.log('---')
return { return {
name: p.getName(), name: p.getName(),
type: checker.typeToString(propType), type: checker.typeToString(propType),
questionToken, questionToken,
typeParams: getAllTypeParameters(propType).map( typeParams: typeParams.map(typeToString),
t => checker.typeToString(t)), filteredTypeParams: typeParams
// classOrIface: propType.isClassOrInterface(), .filter(filterGlobalTypes)
// union: propType.isUnion(), // filter class type parameters
.filter(t => typeParameters.every(tp => tp !== t))
.map(mapGenericTypes)
.filter(filterDuplicates)
.map(typeToString),
} }
})) }))
// output.push(serializeClass(symbol)) // output.push(serializeClass(symbol))