@@ -242,13 +242,13 @@ export function tsArrayLiteralExpression(
242
242
name : string ,
243
243
elementType : ts . TypeNode ,
244
244
values : ( string | number ) [ ] ,
245
- options ?: { export ?: boolean ; readonly ?: boolean } ,
245
+ options ?: { export ?: boolean ; readonly ?: boolean ; injectFooter ?: ts . Node [ ] } ,
246
246
) {
247
247
let variableName = sanitizeMemberName ( name ) ;
248
248
variableName = `${ variableName [ 0 ] . toLowerCase ( ) } ${ variableName . substring ( 1 ) } ` ;
249
249
250
250
const arrayType = options ?. readonly
251
- ? ts . factory . createTypeReferenceNode ( "ReadonlyArray" , [ elementType ] )
251
+ ? tsReadonlyArray ( elementType , options . injectFooter )
252
252
: ts . factory . createArrayTypeNode ( elementType ) ;
253
253
254
254
return ts . factory . createVariableStatement (
@@ -490,3 +490,21 @@ export function tsWithRequired(
490
490
tsUnion ( keys . map ( ( k ) => tsLiteral ( k ) ) ) ,
491
491
] ) ;
492
492
}
493
+
494
+ /**
495
+ * Enhanced ReadonlyArray.
496
+ * eg: type Foo = ReadonlyArray<T>; type Bar = ReadonlyArray<T[]>
497
+ * Foo and Bar are both of type `readonly T[]`
498
+ */
499
+ export function tsReadonlyArray ( type : ts . TypeNode , injectFooter ?: ts . Node [ ] ) : ts . TypeNode {
500
+ if (
501
+ injectFooter &&
502
+ ! injectFooter . some ( ( node ) => ts . isTypeAliasDeclaration ( node ) && node ?. name ?. escapedText === "ReadonlyArray" )
503
+ ) {
504
+ const helper = stringToAST (
505
+ "type ReadonlyArray<T> = [Exclude<T, undefined>] extends [any[]] ? Readonly<Exclude<T, undefined>> : Readonly<Exclude<T, undefined>[]>;" ,
506
+ ) [ 0 ] as any ;
507
+ injectFooter . push ( helper ) ;
508
+ }
509
+ return ts . factory . createTypeReferenceNode ( ts . factory . createIdentifier ( "ReadonlyArray" ) , [ type ] ) ;
510
+ }
0 commit comments