Skip to content

Commit

Permalink
fix(types): fix ExtractedValue type (#4334)
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-tilden authored Feb 19, 2025
1 parent d6bd0cb commit 6e31241
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 8 deletions.
60 changes: 60 additions & 0 deletions src/api/extract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,28 @@ describe('$.extract', () => {
).toStrictEqual({ red: 'red=Four' });
});

it('should correctly type check custom extraction functions returning non-string values', () => {
const $ = load(fixtures.eleven);
const $root = $.root();

expectTypeOf(
$root.extract({
red: {
selector: '.red',
value: (el) => $(el).text().length,
},
}),
).toEqualTypeOf<{ red: number | undefined }>();
expect(
$root.extract({
red: {
selector: '.red',
value: (el) => $(el).text().length,
},
}),
).toStrictEqual({ red: 4 });
});

it('should extract multiple values using custom extraction functions', () => {
const $ = load(fixtures.eleven);
const $root = $.root();
Expand Down Expand Up @@ -215,6 +237,44 @@ describe('$.extract', () => {
});
});

it('should correctly type check nested objects returning non-string values', () => {
const $ = load(fixtures.eleven);
const $root = $.root();

expectTypeOf(
$root.extract({
section: {
selector: 'ul:nth(1)',
value: {
red: {
selector: '.red',
value: (el) => $(el).text().length,
},
},
},
}),
).toEqualTypeOf<{
section: { red: number | undefined } | undefined;
}>();
expect(
$root.extract({
section: {
selector: 'ul:nth(1)',
value: {
red: {
selector: '.red',
value: (el) => $(el).text().length,
},
},
},
}),
).toStrictEqual({
section: {
red: 4,
},
});
});

it('should handle missing href properties without errors (#4239)', () => {
const $ = load(fixtures.eleven);
expect<{ links: string[] }>(
Expand Down
18 changes: 10 additions & 8 deletions src/api/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,24 @@ type ExtractValue = string | ExtractDescriptor | [string | ExtractDescriptor];

export type ExtractMap = Record<string, ExtractValue>;

type ExtractedValue<V extends ExtractValue, M extends ExtractMap> = V extends [
type ExtractedValue<V extends ExtractValue> = V extends [
string | ExtractDescriptor,
]
? NonNullable<ExtractedValue<V[0], M>>[]
? NonNullable<ExtractedValue<V[0]>>[]
: V extends string
? string | undefined
: V extends ExtractDescriptor
? V['value'] extends ExtractMap
? ExtractedMap<V['value']> | undefined
: V['value'] extends ExtractDescriptorFn
? ReturnType<V['value']> | undefined
: ReturnType<typeof prop> | undefined
? V['value'] extends infer U
? U extends ExtractMap
? ExtractedMap<U> | undefined
: U extends ExtractDescriptorFn
? ReturnType<U> | undefined
: ReturnType<typeof prop> | undefined
: never
: never;

export type ExtractedMap<M extends ExtractMap> = {
[key in keyof M]: ExtractedValue<M[key], M>;
[key in keyof M]: ExtractedValue<M[key]>;
};

function getExtractDescr(
Expand Down

0 comments on commit 6e31241

Please sign in to comment.