@@ -86,8 +86,25 @@ export function getObjectDefinition(
8686 return { type } ;
8787}
8888
89- function getPrimitiveType ( type : ts . UnionOrIntersectionType ) {
90- if ( type . types . every ( subtype => subtype . isStringLiteral ( ) || subtype . flags & ts . TypeFlags . StringLiteral ) ) {
89+ function isStringLiteralOrStringIntersection ( subtype : ts . Type , checker : ts . TypeChecker ) : boolean {
90+ // Check if it's a string literal
91+ if ( subtype . isStringLiteral ( ) || subtype . flags & ts . TypeFlags . StringLiteral ) {
92+ return true ;
93+ }
94+ // Check if it's an intersection type that represents "string & something"
95+ // This pattern is used to allow custom strings while providing autocomplete for known literals
96+ if ( subtype . isIntersection ( ) ) {
97+ const stringified = stringifyType ( subtype , checker ) ;
98+ // Match patterns like "string & { _?: undefined; }" or similar
99+ if ( stringified . startsWith ( 'string &' ) ) {
100+ return true ;
101+ }
102+ }
103+ return false ;
104+ }
105+
106+ function getPrimitiveType ( type : ts . UnionOrIntersectionType , checker : ts . TypeChecker ) {
107+ if ( type . types . every ( subtype => isStringLiteralOrStringIntersection ( subtype , checker ) ) ) {
91108 return 'string' ;
92109 }
93110 if ( type . types . every ( subtype => subtype . isNumberLiteral ( ) || subtype . flags & ts . TypeFlags . NumberLiteral ) ) {
@@ -103,9 +120,19 @@ function getUnionTypeDefinition(
103120 checker : ts . TypeChecker
104121) : { type : string ; inlineType : UnionTypeDefinition } {
105122 const valueDescriptions = extractValueDescriptions ( realType , typeNode ) ;
106- const primitiveType = getPrimitiveType ( realType ) ;
123+ const primitiveType = getPrimitiveType ( realType , checker ) ;
107124 const values = realType . types . map ( subtype => {
108- if ( primitiveType && subtype . isStringLiteral ( ) ) {
125+ if ( primitiveType === 'string' ) {
126+ if ( subtype . isStringLiteral ( ) ) {
127+ return ( subtype as ts . LiteralType ) . value . toString ( ) ;
128+ }
129+ // For intersection types like "string & { _?: undefined; }", return "string"
130+ // This indicates that custom string values are allowed
131+ if ( subtype . isIntersection ( ) ) {
132+ return 'string' ;
133+ }
134+ }
135+ if ( primitiveType === 'number' && subtype . isNumberLiteral ( ) ) {
109136 return ( subtype as ts . LiteralType ) . value . toString ( ) ;
110137 }
111138 return stringifyType ( subtype , checker ) ;
0 commit comments