@@ -242,13 +242,13 @@ export function tsArrayLiteralExpression(
242242 name : string ,
243243 elementType : ts . TypeNode ,
244244 values : ( string | number ) [ ] ,
245- options ?: { export ?: boolean ; readonly ?: boolean } ,
245+ options ?: { export ?: boolean ; readonly ?: boolean ; injectFooter ?: ts . Node [ ] } ,
246246) {
247247 let variableName = sanitizeMemberName ( name ) ;
248248 variableName = `${ variableName [ 0 ] . toLowerCase ( ) } ${ variableName . substring ( 1 ) } ` ;
249249
250250 const arrayType = options ?. readonly
251- ? ts . factory . createTypeReferenceNode ( "ReadonlyArray" , [ elementType ] )
251+ ? tsReadonlyArray ( elementType , options . injectFooter )
252252 : ts . factory . createArrayTypeNode ( elementType ) ;
253253
254254 return ts . factory . createVariableStatement (
@@ -490,3 +490,21 @@ export function tsWithRequired(
490490 tsUnion ( keys . map ( ( k ) => tsLiteral ( k ) ) ) ,
491491 ] ) ;
492492}
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