diff --git a/.changeset/dirty-badgers-add.md b/.changeset/dirty-badgers-add.md new file mode 100644 index 0000000000..2688b5406e --- /dev/null +++ b/.changeset/dirty-badgers-add.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Improve types of Struct `pick` to support optional properties diff --git a/packages/effect/src/Struct.ts b/packages/effect/src/Struct.ts index 0b2c0d5b17..1380d69be5 100644 --- a/packages/effect/src/Struct.ts +++ b/packages/effect/src/Struct.ts @@ -7,7 +7,7 @@ import * as Equivalence from "./Equivalence.js" import { dual } from "./Function.js" import * as order from "./Order.js" -import type { MatchRecord, Simplify } from "./Types.js" +import type { MatchRecord, OptionalPropertyOf, RequiredPropertyOf, Simplify } from "./Types.js" /** * Create a new object by picking properties of an existing object. @@ -20,15 +20,17 @@ import type { MatchRecord, Simplify } from "./Types.js" * * @since 2.0.0 */ -export const pick = >( - ...keys: Keys -) => ->( - s: S -): MatchRecord => { +export const pick = >(...keys: Keys) => +>>( + s: Keys[number] extends keyof S ? S : never +): Simplify< + { [K in Keys[number] & OptionalPropertyOf]?: S[K] } & { [K in Keys[number] & RequiredPropertyOf]: S[K] } +> => { const out: any = {} for (const k of keys) { - out[k] = (s as any)[k] + if (k in s) { + out[k] = (s as any)[k] + } } return out } diff --git a/packages/effect/src/Types.ts b/packages/effect/src/Types.ts index 89838b052b..f2eb291a9e 100644 --- a/packages/effect/src/Types.ts +++ b/packages/effect/src/Types.ts @@ -179,3 +179,25 @@ export type Contravariant = (_: A) => void * @since 2.0.0 */ export type MatchRecord = {} extends S ? onTrue : onFalse + +/** + * Extract optional property keys from a record + * @since 2.3.5 + */ +export type OptionalPropertyOf = Exclude< + { + [K in keyof T]: T extends Record ? never : K + }[keyof T], + undefined +> + +/** + * Extract required property keys from a record + * @since 2.3.5 + */ +export type RequiredPropertyOf = Exclude< + { + [K in keyof T]: T extends Record ? K : never + }[keyof T], + undefined +> diff --git a/packages/effect/test/Struct.test.ts b/packages/effect/test/Struct.test.ts index ef2df3e69e..e20258dce7 100644 --- a/packages/effect/test/Struct.test.ts +++ b/packages/effect/test/Struct.test.ts @@ -11,8 +11,8 @@ describe("Struct", () => { it("pick", () => { expect(pipe({ a: "a", b: 1, c: true }, Struct.pick("a", "b"))).toEqual({ a: "a", b: 1 }) - const record: Record = {} - expect(pipe(record, Struct.pick("a", "b"))).toStrictEqual({ a: undefined, b: undefined }) + const record: Record = { a: 1 } + expect(pipe(record, Struct.pick("a", "b"))).toStrictEqual({ a: 1 }) }) it("omit", () => {