{"version":3,"file":"keyof.mjs","sources":["../../src/record/keyof.mts"],"sourcesContent":["import { Arr, expectType, pipe } from 'ts-data-forge';\nimport { type IsNever, type ToString, type UnknownRecord } from 'ts-type-forge';\nimport { enumType } from '../enum/index.mjs';\nimport { undefinedType } from '../primitives/index.mjs';\nimport {\n  type ShapeStructure,\n  type Type,\n  type TypeOf,\n  hasRecordInternals,\n} from '../type.mjs';\n\nexport const keyof = <const R extends UnknownRecord>(\n  recordType: Type<R>,\n  options?: Partial<\n    Readonly<{\n      typeName: string;\n    }>\n  >,\n): KeyofType<R> => {\n  if (!hasRecordInternals(recordType)) {\n    throw new Error(\n      `Expected a record type but received: ${recordType.typeName}`,\n    );\n  }\n\n  // eslint-disable-next-line total-functions/no-unsafe-type-assertion\n  return pipe(getKeysFromStructure(recordType.shapeStructure)).map((keys) =>\n    Arr.isNonEmpty(keys)\n      ? (enumType(keys, {\n          typeName: options?.typeName ?? `keyof ${recordType.typeName}`,\n        }) satisfies Type<string>)\n      : (undefinedType satisfies Type<undefined>),\n  ).value as KeyofType<R>;\n};\n\nconst getKeysFromStructure = (structure: ShapeStructure): readonly string[] => {\n  switch (structure.kind) {\n    case 'simple':\n      return Object.keys(structure.shape);\n\n    case 'union':\n      return getKeysHelper(structure.variants);\n\n    case 'intersection':\n      return getKeysHelper(structure.parts);\n  }\n};\n\nconst getKeysHelper = (\n  structures: readonly ShapeStructure[],\n): readonly string[] => {\n  const mut_allKeys = new Set<string>();\n\n  for (const part of structures) {\n    const keys = getKeysFromStructure(part);\n\n    for (const key of keys) {\n      mut_allKeys.add(key);\n    }\n  }\n\n  return Array.from(mut_allKeys);\n};\n\ntype KeyofType<R extends UnknownRecord> =\n  IsNever<keyof R> extends true ? Type<undefined> : Type<ToString<keyof R>>;\n\n// --- expectType assertions ---\n\n{\n  type Base = Readonly<{ a: 0; b: 1; c: 2 }>;\n\n  // keyof extracts string keys\n  expectType<TypeOf<ReturnType<typeof keyof<Base>>>, 'a' | 'b' | 'c'>('=');\n\n  // keyof of empty record yields undefined\n  type EmptyRecord = Readonly<Record<never, never>>;\n\n  expectType<TypeOf<ReturnType<typeof keyof<EmptyRecord>>>, undefined>('=');\n\n  // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n  expectType<KeyofType<{}>, Type<undefined>>('=');\n\n  expectType<KeyofType<Readonly<{ a: 0; b: 1; c: 2 }>>, Type<'a' | 'b' | 'c'>>(\n    '=',\n  );\n\n  expectType<\n    KeyofType<Readonly<{ x: string; y: number; z: boolean }>>,\n    Type<'x' | 'y' | 'z'>\n  >('=');\n\n  expectType<\n    KeyofType<Readonly<{ same: string; value: string }>>,\n    Type<'same' | 'value'>\n  >('=');\n\n  expectType<KeyofType<Readonly<{ never: never }>>, Type<'never'>>('=');\n}\n"],"names":[],"mappings":";;;;;;;MAWa,KAAA,GAAQ,CACnB,UAAA,EACA,OAAA,KAKiB;AACjB,EAAA,IAAI,CAAC,kBAAA,CAAmB,UAAU,CAAA,EAAG;AACnC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,WAAW,QAAQ,CAAA;AAAA,KAC7D;AAAA,EAAA;AAIF,EAAA,OAAO,IAAA,CAAK,oBAAA,CAAqB,UAAA,CAAW,cAAc,CAAC,CAAA,CAAE,GAAA;AAAA,IAAI,CAAC,IAAA,KAChE,GAAA,CAAI,WAAW,IAAI,CAAA,GACd,SAAS,IAAA,EAAM;AAAA,MACd,QAAA,EAAU,OAAA,EAAS,QAAA,IAAY,CAAA,MAAA,EAAS,WAAW,QAAQ,CAAA;AAAA,KAC5D,CAAA,GACA;AAAA,GACP,CAAE,KAAA;AACJ;AAEA,MAAM,oBAAA,GAAuB,CAAC,SAAA,KAAiD;AAC7E,EAAA,QAAQ,UAAU,IAAA;AAAM,IACtB,KAAK,QAAA;AACH,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AAAA,IAEpC,KAAK,OAAA;AACH,MAAA,OAAO,aAAA,CAAc,UAAU,QAAQ,CAAA;AAAA,IAEzC,KAAK,cAAA;AACH,MAAA,OAAO,aAAA,CAAc,UAAU,KAAK,CAAA;AAAA;AAE1C,CAAA;AAEA,MAAM,aAAA,GAAgB,CACpB,UAAA,KACsB;AACtB,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AAEpC,EAAA,KAAA,MAAW,QAAQ,UAAA,EAAY;AAC7B,IAAA,MAAM,IAAA,GAAO,qBAAqB,IAAI,CAAA;AAEtC,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,WAAA,CAAY,IAAI,GAAG,CAAA;AAAA,IAAA;AACrB,EAAA;AAGF,EAAA,OAAO,KAAA,CAAM,KAAK,WAAW,CAAA;AAC/B,CAAA;;;;"}