import { expectType, Obj } from 'ts-data-forge'; import { type ArrayElement, type UnknownRecord } from 'ts-type-forge'; import { type ExcessPropertyOption, flattenShapeStructure, hasRecordInternals, type Type, type TypeOf, } from '../type.mjs'; import { toUnionKeyString } from '../utils/index.mjs'; import { record } from './record.mjs'; /** Creates a record type with keys picked. */ export const pick = < const R extends UnknownRecord, const KeysToPick extends readonly (keyof R & string)[], >( recordType: Type, keysToPick: KeysToPick, options?: Partial< Readonly<{ typeName: string; excessProperty: ExcessPropertyOption; }> >, ): PickedType => { if (!hasRecordInternals(recordType)) { throw new Error( `Expected a record type but received: ${recordType.typeName}`, ); } const shape = flattenShapeStructure(recordType.shapeStructure); if (shape === undefined) { throw new Error( `pick() requires a simple or intersection record type, but received a union type`, ); } // eslint-disable-next-line total-functions/no-unsafe-type-assertion return record(Obj.pick(shape, keysToPick), { typeName: options?.typeName ?? `Pick<${recordType.typeName}, ${toUnionKeyString(keysToPick)}>`, excessProperty: options?.excessProperty ?? recordType.excessProperty, }) as unknown as PickedType; }; type PickedType< R extends UnknownRecord, KeysToPick extends readonly (keyof R & string)[], > = Type>>>; // --- expectType assertions --- { type Base = ReturnType< typeof record; b: Type<1>; c: Type<2> }>> >; // pick narrows value type to picked keys expectType< TypeOf, readonly ['a', 'b']>>>, Readonly<{ a: 0; b: 1 }> >('='); // pick with single key expectType< TypeOf, readonly ['c']>>>, Readonly<{ c: 2 }> >('='); }