Skip to content
2 changes: 1 addition & 1 deletion src/models/node.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export interface TypeNode {
readonly kind: string;
name: string | undefined;
parentNamespaces: string[];
parentNamespaces: string[] | undefined;
}
4 changes: 3 additions & 1 deletion src/models/types/intersection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ export class IntersectionNode implements TypeNode {

constructor(
public name: string | undefined,
public parentNamespaces: string[],
parentNamespaces: string[],
types: TypeNode[],
public properties: PropertyNode[],
) {
const flatTypes = flattenTypes(types, IntersectionNode);
sortMemberTypes(flatTypes);
this.types = deduplicateMemberTypes(flatTypes);
this.parentNamespaces = name ? parentNamespaces : undefined;
}

types: readonly TypeNode[];
parentNamespaces: string[] | undefined;
}
4 changes: 3 additions & 1 deletion src/models/types/union.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ export class UnionNode implements TypeNode {

constructor(
public name: string | undefined,
public parentNamespaces: string[],
parentNamespaces: string[],
types: TypeNode[],
) {
const flatTypes = flattenTypes(types, UnionNode);
sanitizeBooleanLiterals(flatTypes);
sortMemberTypes(flatTypes);
this.types = deduplicateMemberTypes(flatTypes);
this.parentNamespaces = name ? parentNamespaces : undefined;
}

types: readonly TypeNode[];
parentNamespaces: string[] | undefined;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/parsers/exportParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export function parseExport(
type: ts.Type,
parentNamespaces: string[],
) {
const parsedType = resolveType(type, symbol.getName(), parserContext);
const parsedType = resolveType(type, parserContext);
if (parsedType) {
// Patch parentNamespaces if the type supports it
if (parsedType && 'parentNamespaces' in parsedType) {
Expand Down
9 changes: 3 additions & 6 deletions src/parsers/functionParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,7 @@ function parseFunctionSignature(
parseParameter(parameterSymbol, context, skipResolvingComplexTypes),
);

const returnValueType = resolveType(
signature.getReturnType(),
signature.getDeclaration().name?.getText() || '',
context,
);
const returnValueType = resolveType(signature.getReturnType(), context);

return new CallSignature(parameters, returnValueType);
}
Expand All @@ -94,10 +90,11 @@ function parseParameter(

try {
const parameterDeclaration = parameterSymbol.valueDeclaration as ts.ParameterDeclaration;

const parameterType = resolveType(
checker.getTypeOfSymbolAtLocation(parameterSymbol, parameterSymbol.valueDeclaration!),
parameterSymbol.getName(),
context,
parameterDeclaration.type,
skipResolvingComplexTypes,
);

Expand Down
23 changes: 11 additions & 12 deletions src/parsers/objectParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,31 @@ import ts from 'typescript';
import { parseProperty } from './propertyParser';
import { ParserContext } from '../parser';
import { ObjectNode } from '../models';
import { getTypeNamespaces } from './typeResolver';
import { getTypeName, getTypeNamespaces } from './typeResolver';

export function parseObjectType(
type: ts.Type,
name: string,
context: ParserContext,
skipResolvingComplexTypes: boolean,
): ObjectNode | undefined {
const { shouldInclude, shouldResolveObject, typeStack, includeExternalTypes } = context;
const { shouldInclude, shouldResolveObject, typeStack, includeExternalTypes, checker } = context;

const properties = type
.getProperties()
.filter((property) => includeExternalTypes || !isPropertyExternal(property));

const typeSymbol = type.aliasSymbol ?? type.getSymbol();
let typeName = typeSymbol?.getName();
if (typeName === '__type') {
typeName = undefined;
}
const typeName = getTypeName(type, undefined, checker, false);

if (properties.length) {
if (
!skipResolvingComplexTypes &&
shouldResolveObject({ name, propertyCount: properties.length, depth: typeStack.length })
shouldResolveObject({
name: typeName ?? '',
propertyCount: properties.length,
depth: typeStack.length,
})
) {
const filtered = properties.filter((property) => {
const filteredProperties = properties.filter((property) => {
const declaration =
property.valueDeclaration ??
(property.declarations?.[0] as ts.PropertySignature | undefined);
Expand All @@ -37,11 +36,11 @@ export function parseObjectType(
shouldInclude({ name: property.getName(), depth: typeStack.length + 1 })
);
});
if (filtered.length > 0) {
if (filteredProperties.length > 0) {
return new ObjectNode(
typeName,
getTypeNamespaces(type),
filtered.map((property) => {
filteredProperties.map((property) => {
return parseProperty(
property,
property.valueDeclaration as ts.PropertySignature,
Expand Down
36 changes: 32 additions & 4 deletions src/parsers/propertyParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ export function parseProperty(

try {
let type: ts.Type;

if (propertySignature) {
if (!propertySignature.type) {
type = checker.getAnyType();
} else {
if (propertySignature.type) {
type = checker.getTypeOfSymbolAtLocation(propertySymbol, propertySignature.type);
} else {
type = checker.getAnyType();
}
} else {
type = checker.getTypeOfSymbol(propertySymbol);
Expand All @@ -36,7 +37,12 @@ export function parseProperty(
parsedType = new IntrinsicNode('any');
isOptional = Boolean(propertySignature.questionToken);
} else {
parsedType = resolveType(type, propertySymbol.getName(), context, skipResolvingComplexTypes);
parsedType = resolveType(
type,
context,
isTypeParameterLike(type) ? undefined : propertySignature?.type,
skipResolvingComplexTypes,
);
isOptional = Boolean(propertySymbol.flags & ts.SymbolFlags.Optional);
}

Expand All @@ -58,3 +64,25 @@ export function parseProperty(
parsedSymbolStack.pop();
}
}

function isTypeParameterLike(type: ts.Type): boolean {
// Check if the type is a type parameter
return (
(type.flags & ts.TypeFlags.TypeParameter) !== 0 ||
((type.flags & ts.TypeFlags.Union) !== 0 && isOptionalTypeParameter(type as ts.UnionType))
);
}

function isOptionalTypeParameter(type: ts.UnionType): boolean {
// Check if the type is defined as
// foo?: T
// where T is a type parameter

return (
type.types.length === 2 &&
type.types.some((t) => t.flags & ts.TypeFlags.Undefined) &&
type.types.some(
(t) => 'objectFlags' in t && ((t.objectFlags as number) & ts.ObjectFlags.Instantiated) !== 0,
)
);
}
Loading