@@ -334,7 +334,7 @@ function deriveSchemaTypeIfTsArrayType(tsType: ts.Type, typePath: TypePathSegmen
334334}
335335
336336function deriveSchemaTypeIfScalarType ( tsType : ts . Type , context : TypeDerivationContext ) : Result < schema . TypeDefinition , string [ ] > | undefined {
337- if ( tsutils . isIntrinsicBooleanType ( tsType ) ) {
337+ if ( tsutils . isIntrinsicBooleanType ( tsType ) || isBooleanUnionType ( tsType ) ) {
338338 context . scalarTypeDefinitions [ schema . BuiltInScalarTypeName . Boolean ] = { } ;
339339 return new Ok ( { type : "named" , kind : "scalar" , name : schema . BuiltInScalarTypeName . Boolean } ) ;
340340 }
@@ -394,6 +394,18 @@ function isMapType(tsType: ts.Type): boolean {
394394 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 ;
395395}
396396
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+
397409function isJSONValueType ( tsType : ts . Type , ndcLambdaSdkModule : ts . ResolvedModuleFull ) : boolean {
398410 // Must be a class type
399411 if ( ! tsutils . isObjectType ( tsType ) || ! tsutils . isObjectFlagSet ( tsType , ts . ObjectFlags . Class ) )
@@ -411,7 +423,7 @@ function isJSONValueType(tsType: ts.Type, ndcLambdaSdkModule: ts.ResolvedModuleF
411423}
412424
413425function 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 ) ;
415427 if ( notNullableResult !== null ) {
416428 const [ notNullableType , nullOrUndefinability ] = notNullableResult ;
417429 return deriveSchemaTypeForTsType ( notNullableType , typePath , context , recursionDepth + 1 )
@@ -456,11 +468,11 @@ function unwrapPromiseType(tsType: ts.Type, typeChecker: ts.TypeChecker): ts.Typ
456468 }
457469}
458470
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 ;
461473
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 ;
464476 const nullOrUndefinability =
465477 isNullable
466478 ? ( isUndefined
@@ -472,12 +484,21 @@ function unwrapNullableType(ty: ts.Type): [ts.Type, schema.NullOrUndefinability]
472484 : null
473485 ) ;
474486
475- const typesWithoutNullAndUndefined = ty . types
487+ const typesWithoutNullAndUndefined = tsType . types
476488 . filter ( t => ! tsutils . isIntrinsicNullType ( t ) && ! tsutils . isIntrinsicUndefinedType ( t ) ) ;
477489
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+ }
481502}
482503
483504type PropertyTypeInfo = {
0 commit comments