Skip to content

Commit 8729cdb

Browse files
fix: conditional type generation (#30)
1 parent baa4b40 commit 8729cdb

1 file changed

Lines changed: 131 additions & 8 deletions

File tree

generate-types/index.js

Lines changed: 131 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -540,8 +540,9 @@ const printError = (diagnostic) => {
540540
/** @typedef {{ type: "index", symbolName: SymbolName, objectType: ts.Type, indexType: ts.Type }} ParsedIndexType */
541541
/** @typedef {{ type: "template", texts: readonly string[], types: readonly ts.Type[] }} ParsedTemplateType */
542542
/** @typedef {{ type: "import", symbolName: SymbolName, exportName: string, from: string, isValue: boolean }} ParsedImportType */
543-
/** @typedef {{ type: "symbol", symbolName: SymbolName }} ParsedSymbolType */
544-
/** @typedef {ParsedPrimitiveType | ParsedTypeParameterType | ParsedTupleType | ParsedInterfaceType | ParsedReferenceType | ParsedUnionType | ParsedIntersectionType | ParsedIndexType | ParsedTemplateType | ParsedImportType | ParsedSymbolType} ParsedType */
543+
/** @typedef {{ type: "symbol", symbolName: SymbolName}} ParsedSymbolType */
544+
/** @typedef {{ type: "conditional", symbolName: SymbolName, checkType: ts.Type, extendsType: ts.Type, trueType: ts.Type, falseType: ts.Type, typeParameters?: readonly ts.Type[] }} ParsedConditionalType */
545+
/** @typedef {ParsedPrimitiveType | ParsedTypeParameterType | ParsedTupleType | ParsedInterfaceType | ParsedReferenceType | ParsedUnionType | ParsedIntersectionType | ParsedIndexType | ParsedTemplateType | ParsedImportType | ParsedSymbolType | ParsedConditionalType} ParsedType */
545546
/** @typedef {ParsedType | MergedClassType | MergedNamespaceType} MergedType */
546547

547548
const isExcluded = (name) => {
@@ -848,6 +849,8 @@ const printError = (diagnostic) => {
848849
return argsWithoutDefaults;
849850
};
850851

852+
const symbol = type.aliasSymbol || type.getSymbol();
853+
851854
if (/** @type {any} */ (type).isTypeParameter()) {
852855
return {
853856
type: "typeParameter",
@@ -1053,8 +1056,6 @@ const printError = (diagnostic) => {
10531056
};
10541057
}
10551058

1056-
const symbol = type.aliasSymbol || type.getSymbol();
1057-
10581059
if (objectFlags & ts.ObjectFlags.Reference) {
10591060
const typeRef = /** @type {ts.TypeReference} */ (type);
10601061
const typeArguments = checker.getTypeArguments(typeRef);
@@ -1200,6 +1201,38 @@ const printError = (diagnostic) => {
12001201
};
12011202
}
12021203

1204+
if (flags & ts.TypeFlags.Conditional) {
1205+
const { checkType, extendsType, root } =
1206+
/** @type {ts.ConditionalType} */ (type);
1207+
1208+
return {
1209+
type: "conditional",
1210+
symbolName,
1211+
checkType,
1212+
extendsType,
1213+
trueType: checker.getTypeAtLocation(root.node.trueType),
1214+
falseType: checker.getTypeAtLocation(root.node.falseType),
1215+
typeParameters:
1216+
type.aliasTypeArguments && type.aliasTypeArguments.length > 0
1217+
? type.aliasTypeArguments
1218+
: undefined,
1219+
// skip: true,
1220+
};
1221+
}
1222+
1223+
if (flags & ts.TypeFlags.IndexedAccess) {
1224+
const { objectType, indexType } = /** @type {ts.IndexedAccessType} */ (
1225+
type
1226+
);
1227+
1228+
return {
1229+
type: "index",
1230+
symbolName: parseName(type),
1231+
objectType,
1232+
indexType,
1233+
};
1234+
}
1235+
12031236
const declaration = getDeclaration(symbol);
12041237

12051238
return {
@@ -1289,6 +1322,12 @@ const printError = (diagnostic) => {
12891322
captureType(type, inner);
12901323
}
12911324
break;
1325+
case "conditional":
1326+
captureType(type, parsed.checkType);
1327+
captureType(type, parsed.extendsType);
1328+
captureType(type, parsed.trueType);
1329+
captureType(type, parsed.falseType);
1330+
break;
12921331
case "interface":
12931332
for (const prop of parsed.baseTypes) {
12941333
typeUsedAsBaseType.add(prop);
@@ -1651,6 +1690,18 @@ const printError = (diagnostic) => {
16511690
return x === undefined ? item : x;
16521691
});
16531692
}
1693+
case "conditional": {
1694+
const { symbolName, checkType, extendsType, trueType, falseType } =
1695+
parsed;
1696+
return [
1697+
parsed.type,
1698+
symbolName[0],
1699+
checkType,
1700+
extendsType,
1701+
trueType,
1702+
falseType,
1703+
];
1704+
}
16541705
case "interface": {
16551706
const {
16561707
symbolName,
@@ -1770,6 +1821,12 @@ const printError = (diagnostic) => {
17701821
const needName = [];
17711822
for (const [type, parsed] of parsedCollectedTypes) {
17721823
switch (parsed.type) {
1824+
case "conditional": {
1825+
if (parsed.typeParameters) {
1826+
needName.push([type, parsed]);
1827+
}
1828+
break;
1829+
}
17731830
case "interface": {
17741831
if (
17751832
parsed.typeParameters ||
@@ -2157,6 +2214,7 @@ const printError = (diagnostic) => {
21572214
return parsed.name;
21582215
case "typeParameter": {
21592216
let code = parsed.name;
2217+
21602218
if (state === "in type args") {
21612219
if (parsed.constraint) {
21622220
const constraint = getCode(parsed.constraint, typeArgs, state);
@@ -2167,6 +2225,7 @@ const printError = (diagnostic) => {
21672225
code += ` = ${defaultValue}`;
21682226
}
21692227
}
2228+
21702229
return code;
21712230
}
21722231
case "template": {
@@ -2180,10 +2239,21 @@ const printError = (diagnostic) => {
21802239
return code;
21812240
}
21822241
case "index": {
2183-
return `(${getCode(parsed.objectType, typeArgs)})[${getCode(
2184-
parsed.indexType,
2185-
typeArgs,
2186-
)}]`;
2242+
const { objectType, indexType } = parsed;
2243+
const getIndexTypeCode = () => {
2244+
if (indexType.flags & ts.TypeFlags.Substitution) {
2245+
const { baseType } = /** @type {ts.SubstitutionType} */ (indexType);
2246+
const symbol = baseType.getSymbol();
2247+
if (symbol) {
2248+
const name = symbol.getName();
2249+
if (name !== "__type" && name !== "unknown") return name;
2250+
}
2251+
}
2252+
2253+
return getCode(indexType, typeArgs);
2254+
};
2255+
2256+
return `(${getCode(objectType, typeArgs)})[${getIndexTypeCode()}]`;
21872257
}
21882258
case "union":
21892259
case "intersection": {
@@ -2261,6 +2331,59 @@ const printError = (diagnostic) => {
22612331
"with type args",
22622332
)}${typeArgumentsWithoutDefaults}`;
22632333
}
2334+
case "conditional": {
2335+
const getExtendsString = (t) => {
2336+
if (t.flags & ts.TypeFlags.TypeParameter) {
2337+
const symbol = t.getSymbol();
2338+
if (
2339+
symbol &&
2340+
symbol.declarations &&
2341+
symbol.declarations.length > 0
2342+
) {
2343+
const decl = symbol.declarations[0];
2344+
if (
2345+
ts.isTypeParameterDeclaration(decl) &&
2346+
decl.parent &&
2347+
ts.isInferTypeNode(decl.parent)
2348+
) {
2349+
return "infer " + symbol.getName();
2350+
}
2351+
}
2352+
return symbol ? symbol.getName() : getCode(t, typeArgs, state);
2353+
}
2354+
2355+
if (checker.isArrayType(t)) {
2356+
const elementType = t.typeArguments[0];
2357+
return "(" + getExtendsString(elementType) + ")[]";
2358+
}
2359+
2360+
return getCode(t, typeArgs, state);
2361+
};
2362+
2363+
const checkType = getCode(parsed.checkType, typeArgs, state);
2364+
const extendsType = getExtendsString(parsed.extendsType);
2365+
const trueType = getCode(parsed.trueType, typeArgs, state);
2366+
const falseType = getCode(parsed.falseType, typeArgs, state);
2367+
2368+
const variable = typeToVariable.get(type);
2369+
if (variable !== undefined) {
2370+
queueDeclaration(
2371+
type,
2372+
variable,
2373+
() =>
2374+
`type ${variable}${
2375+
parsed.typeParameters
2376+
? `<${parsed.typeParameters
2377+
.map((t) => getCode(t, new Set(), "in type args"))
2378+
.join(", ")}>`
2379+
: ""
2380+
} = (${checkType} extends ${extendsType} ? ${trueType} : ${falseType});`,
2381+
);
2382+
return `${variable}`;
2383+
}
2384+
2385+
return `(${checkType} extends ${extendsType} ? ${trueType} : ${falseType})`;
2386+
}
22642387
case "interface": {
22652388
const variable = typeToVariable.get(type);
22662389
if (variable !== undefined) {

0 commit comments

Comments
 (0)