orval
orval copied to clipboard
Issues with NonReadonly types and array types
When an object contains a field with an array type, the NonReadonly wrapped type cannot be assinged to an array.
For Reference (this is generated for NonReadonly type:
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;
type WritableKeys<T> = { [P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P> }[keyof T];
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
type DistributeReadOnlyOverUnions<T> = T extends any ? NonReadonly<T> : never;
type Writable<T> = Pick<T, WritableKeys<T>>;
type NonReadonly<T> = [T] extends [UnionToIntersection<T>]
? { [P in keyof Writable<T>]: T[P] extends object ? NonReadonly<NonNullable<T[P]>> : T[P] }
: DistributeReadOnlyOverUnions<T>;
What are the steps to reproduce this issue?
Example:
type Name = { first: string, last: string };
type NamesObject = { names: Name[] };
const namesObj: NonReadonly<NamesObject> = { names: [{ first: 'a', last: 'b' }] }; // This works.
// Type '{ [x: number]: { first: string; last: string; }; }' is missing the following properties from
// type 'Name[]': length, pop, push, concat, and 29 more.ts(2740)
const names: Name[] = namesObj.names; // This does not.
More Information
The problems goes away if the NonReadonly type is defined as below.:
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;
type WritableKeys<T> = { [P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P> }[keyof T];
type Writable<T> = Pick<T, WritableKeys<T>>;
type NonReadonly<T> = {
[P in keyof Writable<T>]: T[P] extends number | string | boolean | undefined | null
? T[P]
: NonReadonly<NonNullable<T[P]>>;
};
This was how it was defined in an earlier version of #813. See also: https://github.com/anymaniax/orval/pull/813#issuecomment-1502923146
Additional Question
Can the NonReadonly type be exported? This would come in handy when extending generated code.
What versions are you using?
Operating System: Win 10 Package Version: 6.14.3
Hello @localrobot, for me in both case I have the problem
This one seems to work properly no?
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <
T,
>() => T extends Y ? 1 : 2
? A
: B;
type WritableKeys<T> = {
[P in keyof T]: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>;
}[keyof T];
type DeepWritablePrimitive =
| undefined
| null
| boolean
| string
| number
| Function;
type DeepWritable<T> = T extends DeepWritablePrimitive
? T
: T extends Array<infer U>
? DeepWritableArray<U>
: T extends Map<infer K, infer V>
? DeepWritableMap<K, V>
: T extends Set<infer T>
? DeepWriableSet<T>
: DeepWritableObject<T>;
type DeepWritableArray<T> = Array<DeepWritable<T>>;
type DeepWritableMap<K, V> = Map<K, DeepWritable<V>>;
type DeepWriableSet<T> = Set<DeepWritable<T>>;
type DeepWritableObject<T> = {
[K in WritableKeys<T>]: DeepWritable<T[K]>;
};
type Name = { first: string; last: string };
type NamesObject = { names: Name[]; readonly id: string };
const namesObj: DeepWritable<NamesObject> = {
names: [{ first: 'a', last: 'b' }],
};
const names: Name[] = namesObj.names;
I think we can also omit empty value type with this
type EmptyKeys<T> = {
[P in keyof T]: {} extends T[P] ? P : [{}] extends T[P] ? P : never;
}[keyof T];
type OmitEmptyKeys<T> = Omit<T, EmptyKeys<T>>;
type DeepWritableWithoutEmpty<T> = OmitEmptyKeys<DeepWritable<T>>;
all coming from here
Those also seem to have problems:
type Name = { first: string; last: string, address?: { line1: string, line2?: string } };
type NamesObject = { names: Name[]; readonly id: string };
// Property 'address' is missing in type '{ first: string; last: string; }' but required in type
// 'DeepWritableObject<Name>'.ts(2741)
const namesObj: DeepWritable<NamesObject> = {
names: [{ first: 'a', last: 'b' }],
};
const names: Name[] = namesObj.names;
// Property 'line2' is missing in type '{ line1: string; }' but required in type
// 'DeepWritableObject<{ line1: string; line2?: string | undefined; }>'.ts(2741)
const name: DeepWritable<Name> = { first: 'a', last: 'b', address: { line1: 'abc' } }
So where are we with this ticket?