@@ -334,7 +334,7 @@ function deriveSchemaTypeIfTsArrayType(tsType: ts.Type, typePath: TypePathSegmen
334
334
}
335
335
336
336
function deriveSchemaTypeIfScalarType ( tsType : ts . Type , context : TypeDerivationContext ) : Result < schema . TypeDefinition , string [ ] > | undefined {
337
- if ( tsutils . isIntrinsicBooleanType ( tsType ) ) {
337
+ if ( tsutils . isIntrinsicBooleanType ( tsType ) || isBooleanUnionType ( tsType ) ) {
338
338
context . scalarTypeDefinitions [ schema . BuiltInScalarTypeName . Boolean ] = { } ;
339
339
return new Ok ( { type : "named" , kind : "scalar" , name : schema . BuiltInScalarTypeName . Boolean } ) ;
340
340
}
@@ -394,6 +394,18 @@ function isMapType(tsType: ts.Type): boolean {
394
394
return symbol . escapedName === "Map" && symbol . members ?. has ( ts . escapeLeadingUnderscores ( "keys" ) ) === true && symbol . members ?. has ( ts . escapeLeadingUnderscores ( "values" ) ) === true && symbol . members ?. has ( ts . escapeLeadingUnderscores ( "entries" ) ) === true ;
395
395
}
396
396
397
+ // Identifies the 'true | false' type (which is distinct from the 'boolean' type)
398
+ function isBooleanUnionType ( tsType : ts . Type ) : boolean {
399
+ if ( ! tsutils . isUnionType ( tsType ) ) return false ;
400
+
401
+ return tsType . types . length === 2 && unionTypeContainsBothBooleanLiterals ( tsType ) ;
402
+ }
403
+
404
+ function unionTypeContainsBothBooleanLiterals ( tsUnionType : ts . UnionType ) : boolean {
405
+ return tsUnionType . types . find ( tsType => tsutils . isBooleanLiteralType ( tsType ) && tsType . intrinsicName === "true" ) !== undefined
406
+ && tsUnionType . types . find ( tsType => tsutils . isBooleanLiteralType ( tsType ) && tsType . intrinsicName === "false" ) !== undefined ;
407
+ }
408
+
397
409
function isJSONValueType ( tsType : ts . Type , ndcLambdaSdkModule : ts . ResolvedModuleFull ) : boolean {
398
410
// Must be a class type
399
411
if ( ! tsutils . isObjectType ( tsType ) || ! tsutils . isObjectFlagSet ( tsType , ts . ObjectFlags . Class ) )
@@ -411,7 +423,7 @@ function isJSONValueType(tsType: ts.Type, ndcLambdaSdkModule: ts.ResolvedModuleF
411
423
}
412
424
413
425
function deriveSchemaTypeIfNullableType ( tsType : ts . Type , typePath : TypePathSegment [ ] , context : TypeDerivationContext , recursionDepth : number ) : Result < schema . TypeDefinition , string [ ] > | undefined {
414
- const notNullableResult = unwrapNullableType ( tsType ) ;
426
+ const notNullableResult = unwrapNullableType ( tsType , context . typeChecker ) ;
415
427
if ( notNullableResult !== null ) {
416
428
const [ notNullableType , nullOrUndefinability ] = notNullableResult ;
417
429
return deriveSchemaTypeForTsType ( notNullableType , typePath , context , recursionDepth + 1 )
@@ -456,11 +468,11 @@ function unwrapPromiseType(tsType: ts.Type, typeChecker: ts.TypeChecker): ts.Typ
456
468
}
457
469
}
458
470
459
- function unwrapNullableType ( ty : ts . Type ) : [ ts . Type , schema . NullOrUndefinability ] | null {
460
- if ( ! ty . isUnion ( ) ) return null ;
471
+ function unwrapNullableType ( tsType : ts . Type , typeChecker : ts . TypeChecker ) : [ ts . Type , schema . NullOrUndefinability ] | null {
472
+ if ( ! tsType . isUnion ( ) ) return null ;
461
473
462
- const isNullable = ty . types . find ( tsutils . isIntrinsicNullType ) !== undefined ;
463
- const isUndefined = ty . types . find ( tsutils . isIntrinsicUndefinedType ) !== undefined ;
474
+ const isNullable = tsType . types . find ( tsutils . isIntrinsicNullType ) !== undefined ;
475
+ const isUndefined = tsType . types . find ( tsutils . isIntrinsicUndefinedType ) !== undefined ;
464
476
const nullOrUndefinability =
465
477
isNullable
466
478
? ( isUndefined
@@ -472,12 +484,21 @@ function unwrapNullableType(ty: ts.Type): [ts.Type, schema.NullOrUndefinability]
472
484
: null
473
485
) ;
474
486
475
- const typesWithoutNullAndUndefined = ty . types
487
+ const typesWithoutNullAndUndefined = tsType . types
476
488
. filter ( t => ! tsutils . isIntrinsicNullType ( t ) && ! tsutils . isIntrinsicUndefinedType ( t ) ) ;
477
489
478
- return typesWithoutNullAndUndefined . length === 1 && nullOrUndefinability
479
- ? [ typesWithoutNullAndUndefined [ 0 ] ! , nullOrUndefinability ]
480
- : null ;
490
+ // The case where one type is unioned with either or both of null and undefined
491
+ if ( typesWithoutNullAndUndefined . length === 1 && nullOrUndefinability ) {
492
+ return [ typesWithoutNullAndUndefined [ 0 ] ! , nullOrUndefinability ] ;
493
+ }
494
+ // The weird edge case where null or undefined is unioned with both 'true' and 'false'
495
+ // We simplify this to being unioned with 'boolean' instead
496
+ else if ( nullOrUndefinability && typesWithoutNullAndUndefined . length === 2 && unionTypeContainsBothBooleanLiterals ( tsType ) ) {
497
+ return [ typeChecker . getBooleanType ( ) , nullOrUndefinability ] ;
498
+ }
499
+ else {
500
+ return null ;
501
+ }
481
502
}
482
503
483
504
type PropertyTypeInfo = {
0 commit comments