Skip to content

Commit

Permalink
Improve types of Struct pick to support optional properties
Browse files Browse the repository at this point in the history
  • Loading branch information
evelant committed Feb 13, 2024
1 parent 2b62548 commit a128165
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-badgers-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": patch
---

Improve types of Struct `pick` to support optional properties
18 changes: 10 additions & 8 deletions packages/effect/src/Struct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -20,15 +20,17 @@ import type { MatchRecord, Simplify } from "./Types.js"
*
* @since 2.0.0
*/
export const pick = <Keys extends Array<PropertyKey>>(
...keys: Keys
) =>
<S extends Record<Keys[number], any>>(
s: S
): MatchRecord<S, { [K in Keys[number]]: S[K] | undefined }, { [K in Keys[number]]: S[K] }> => {
export const pick = <Keys extends Array<PropertyKey>>(...keys: Keys) =>
<S extends Partial<Record<Keys[number], any>>>(
s: Keys[number] extends keyof S ? S : never
): Simplify<
{ [K in Keys[number] & OptionalPropertyOf<S>]?: S[K] } & { [K in Keys[number] & RequiredPropertyOf<S>]: 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
}
Expand Down
22 changes: 22 additions & 0 deletions packages/effect/src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,25 @@ export type Contravariant<A> = (_: A) => void
* @since 2.0.0
*/
export type MatchRecord<S, onTrue, onFalse> = {} extends S ? onTrue : onFalse

/**
* Extract optional property keys from a record
* @since 2.3.5
*/
export type OptionalPropertyOf<T extends object> = Exclude<
{
[K in keyof T]: T extends Record<K, T[K]> ? never : K
}[keyof T],
undefined
>

/**
* Extract required property keys from a record
* @since 2.3.5
*/
export type RequiredPropertyOf<T extends object> = Exclude<
{
[K in keyof T]: T extends Record<K, T[K]> ? K : never
}[keyof T],
undefined
>
4 changes: 2 additions & 2 deletions packages/effect/test/Struct.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, number> = {}
expect(pipe(record, Struct.pick("a", "b"))).toStrictEqual({ a: undefined, b: undefined })
const record: Record<string, number> = { a: 1 }
expect(pipe(record, Struct.pick("a", "b"))).toStrictEqual({ a: 1 })
})

it("omit", () => {
Expand Down

0 comments on commit a128165

Please sign in to comment.