diff --git a/source/set-field-type.d.ts b/source/set-field-type.d.ts index 973cad6fa..ecbe713ea 100644 --- a/source/set-field-type.d.ts +++ b/source/set-field-type.d.ts @@ -1,6 +1,16 @@ -import type {Except} from './except'; import type {Simplify} from './simplify'; +type SetFieldTypeOptions = { + /** + Preserve optional and readonly modifiers for properties being updated. + + NOTE: Property modifiers will always be preserved for properties that are not being updated. + + @default true + */ + preservePropertyModifiers?: boolean; +}; + /** Create a type that changes the type of the given keys. @@ -15,23 +25,33 @@ Use-cases: import type {SetFieldType} from 'type-fest'; type MyModel = { - id: number; - createdAt: Date; - updatedAt: Date; + readonly id: number; + readonly createdAt: Date; + updatedAt?: Date; }; type MyModelApi = SetFieldType; // { -// id: number; -// createdAt: string; -// updatedAt: string; +// readonly id: number; +// readonly createdAt: string; +// updatedAt?: string; +// } + +// `preservePropertyModifiers` option can be set to `false` if you want to remove property modifiers for properties being updated +type MyModelApi = SetFieldType; +// { +// readonly id: number; +// createdAt: string; // no longer readonly +// updatedAt: string; // no longer optional // } ``` @category Object */ -export type SetFieldType = - Simplify< - Except & - Record - >; +export type SetFieldType = + Simplify<{ + [P in keyof BaseType]: P extends Keys ? NewType : BaseType[P]; + } & ( + // `Record` is used to remove property modifiers + Options['preservePropertyModifiers'] extends false ? Record : unknown + )>; diff --git a/test-d/set-field-type.ts b/test-d/set-field-type.ts index 10f4b154d..ef6615cd9 100644 --- a/test-d/set-field-type.ts +++ b/test-d/set-field-type.ts @@ -12,3 +12,29 @@ expectType<{a: string; b: number; c: number}>(variation3); declare const variation4: SetFieldType<{a: string; b: string; c: string}, 'b', number>; expectNotAssignable<{a: string; b: string; c: string}>(variation4); + +// Works with union types +declare const variation5: SetFieldType<{a: string; b: string} | {a: number; c: number}, 'a', boolean>; +expectType<{a: boolean; b: string} | {a: boolean; c: number}>(variation5); + +declare const variation6: SetFieldType<{a: string; b: string} | {a: number; c: number}, 'a', boolean, {preservePropertyModifiers: false}>; +expectType<{a: boolean; b: string} | {a: boolean; c: number}>(variation6); + +// Property modifiers are always preserved for properties that are not being updated +declare const variation7: SetFieldType<{a?: string; readonly b: string; c: string}, 'c', number>; +expectType<{a?: string; readonly b: string; c: number}>(variation7); + +declare const variation8: SetFieldType<{a?: string; readonly b: string; c: string}, 'c', number, {preservePropertyModifiers: false}>; +expectType<{a?: string; readonly b: string; c: number}>(variation8); + +// Preserves property modifiers +declare const variation9: SetFieldType<{a?: string; readonly b: string; readonly c?: string}, 'a' | 'c', number>; +expectType<{a?: number; readonly b: string; readonly c?: number}>(variation9); + +// Doesn't preserve property modifiers when `preservePropertyModifiers` is `false` +declare const variation10: SetFieldType<{a?: string; readonly b: string; readonly c?: string}, 'a' | 'c', number, {preservePropertyModifiers: false}>; +expectType<{a: number; readonly b: string; c: number}>(variation10); + +// Falls back to default of `true`, if `preservePropertyModifiers` is set to `boolean` +declare const variation11: SetFieldType<{a?: string; readonly b: string; readonly c?: string}, 'a' | 'c', number, {preservePropertyModifiers: boolean}>; +expectType<{a?: number; readonly b: string; readonly c?: number}>(variation11);