Skip to content

Commit

Permalink
Make ArrayValue type distributive
Browse files Browse the repository at this point in the history
  • Loading branch information
axelboc committed Jan 23, 2025
1 parent 77b3ebf commit 60462e7
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 49 deletions.
1 change: 1 addition & 0 deletions eslint.config.base.js
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,7 @@ export function createConfig(opts = {}) {
'@typescript-eslint/no-unnecessary-parameter-property-assignment':
'warn',
'@typescript-eslint/no-unnecessary-qualifier': 'warn',
'@typescript-eslint/no-unnecessary-type-arguments': 'warn', // downgrade
'@typescript-eslint/no-unsafe-argument': 'warn',
'@typescript-eslint/no-unsafe-assignment': 'off', // too tricky to fix when `any` is declared externally (JSON.parse, Array.isArray, etc.)
'@typescript-eslint/no-unused-vars': [
Expand Down
19 changes: 10 additions & 9 deletions packages/app/src/vis-packs/core/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { createMemo } from '@h5web/shared/createMemo';
import { assertDatasetValue, isDefined } from '@h5web/shared/guards';
import {
type ArrayShape,
type ArrayValue,
type Dataset,
type ScalarShape,
type Value,
} from '@h5web/shared/hdf5-models';
import { type NumArray } from '@h5web/shared/vis-models';
import { castArray } from '@h5web/shared/vis-utils';
import { type NdArray, type TypedArray } from 'ndarray';
import { type NdArray } from 'ndarray';
import { useMemo } from 'react';

import { type DimensionMapping } from '../../dimension-mapper/models';
Expand Down Expand Up @@ -100,29 +101,29 @@ export function useDatasetValues<D extends Dataset<ArrayShape | ScalarShape>>(
});
}

export function useBaseArray<T extends unknown[] | TypedArray | undefined>(
export function useBaseArray<T extends ArrayValue | undefined>(
value: T,
rawDims: number[],
): T extends unknown[] | TypedArray ? NdArray<T> : undefined;
): T extends ArrayValue ? NdArray<T> : undefined;

export function useBaseArray(
value: unknown[] | TypedArray | undefined,
value: ArrayValue | undefined,
rawDims: number[],
): NdArray<unknown[] | TypedArray> | undefined {
): NdArray<ArrayValue> | undefined {
return useMemo(() => getBaseArray(value, rawDims), [value, rawDims]);
}

export function useMappedArray<T extends unknown[] | TypedArray | undefined>(
export function useMappedArray<T extends ArrayValue | undefined>(
value: T,
dims: number[],
mapping: DimensionMapping,
): T extends unknown[] | TypedArray ? NdArray<T> : undefined;
): T extends ArrayValue ? NdArray<T> : undefined;

export function useMappedArray(
value: unknown[] | TypedArray | undefined,
value: ArrayValue | undefined,
dims: number[],
mapping: DimensionMapping,
): NdArray<unknown[] | TypedArray> | undefined {
): NdArray<ArrayValue> | undefined {
const baseArray = useBaseArray(value, dims);

return useMemo(() => applyMapping(baseArray, mapping), [baseArray, mapping]);
Expand Down
28 changes: 15 additions & 13 deletions packages/app/src/vis-packs/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
type NumArray,
} from '@h5web/shared/vis-models';
import { createArrayFromView } from '@h5web/shared/vis-utils';
import ndarray, { type NdArray, type TypedArray } from 'ndarray';
import ndarray, { type NdArray } from 'ndarray';

import { type DimensionMapping } from '../../dimension-mapper/models';
import { isAxis } from '../../dimension-mapper/utils';
Expand All @@ -32,29 +32,27 @@ export const INTERACTIONS_WITH_AXIAL_ZOOM = [
{ shortcut: 'Ctrl+Shift+Drag', description: 'Select to zoom in Y' },
];

export function getBaseArray<T extends unknown[] | TypedArray | undefined>(
export function getBaseArray<T extends ArrayValue | undefined>(
value: T,
rawDims: number[],
): T extends unknown[] | TypedArray ? NdArray<T> : undefined;
): T extends ArrayValue ? NdArray<T> : undefined;

export function getBaseArray(
value: unknown[] | TypedArray | undefined,
value: ArrayValue | undefined,
rawDims: number[],
): NdArray<unknown[] | TypedArray> | undefined {
): NdArray<ArrayValue> | undefined {
return value && ndarray(value, rawDims);
}

export function applyMapping<
T extends NdArray<unknown[] | TypedArray> | undefined,
>(
export function applyMapping<T extends NdArray<ArrayValue> | undefined>(
baseArray: T,
mapping: (number | Axis | ':')[],
): T extends NdArray<unknown[] | TypedArray> ? T : undefined;
): T extends NdArray<ArrayValue> ? T : undefined;

export function applyMapping(
baseArray: NdArray<unknown[] | TypedArray> | undefined,
baseArray: NdArray<ArrayValue> | undefined,
mapping: (number | Axis | ':')[],
): NdArray<unknown[] | TypedArray> | undefined {
): NdArray<ArrayValue> | undefined {
if (!baseArray) {
return undefined;
}
Expand Down Expand Up @@ -95,12 +93,16 @@ export function getImageInteractions(keepRatio: boolean): InteractionInfo[] {
return keepRatio ? BASE_INTERACTIONS : INTERACTIONS_WITH_AXIAL_ZOOM;
}

function isBoolArray(val: ArrayValue<NumericLikeType>): val is boolean[] {
return Array.isArray(val) && typeof val[0] === 'boolean';
}

export function toNumArray(arr: ArrayValue<NumericLikeType>): NumArray {
if (typeof arr[0] === 'boolean') {
if (isBoolArray(arr)) {
return arr.map((val) => (val ? 1 : 0));
}

return arr as NumArray;
return arr;
}

const TYPE_STRINGS: Record<NumericLikeType['class'], string> = {
Expand Down
14 changes: 4 additions & 10 deletions packages/shared/src/guards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,6 @@ export function isTypedArray(val: unknown): val is TypedArray {
);
}

export function assertArrayOrTypedArray(
val: unknown,
): asserts val is unknown[] | TypedArray {
if (!Array.isArray(val) && !isTypedArray(val)) {
throw new TypeError('Expected array or typed array');
}
}

export function isGroup(entity: Entity): entity is Group {
return entity.kind === EntityKind.Group;
}
Expand Down Expand Up @@ -431,7 +423,7 @@ export function isComplexValue(
function assertPrimitiveValue(
type: DType,
value: unknown,
): asserts value is Primitive<DType> {
): asserts value is Primitive {
if (isNumericType(type)) {
assertNum(value);
} else if (isStringType(type)) {
Expand All @@ -457,7 +449,9 @@ export function assertDatasetValue<D extends Dataset<ScalarShape | ArrayShape>>(
if (hasScalarShape(dataset)) {
assertPrimitiveValue(type, value);
} else {
assertArrayOrTypedArray(value);
if (!Array.isArray(value) && !isTypedArray(value)) {
throw new TypeError('Expected array or typed array');
}

if (value.length > 0) {
assertPrimitiveValue(type, value[0]);
Expand Down
24 changes: 11 additions & 13 deletions packages/shared/src/hdf5-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,21 +207,19 @@ export interface UnknownType {
/* ----------------- */
/* ----- VALUE ----- */

export type Primitive<T extends DType> = T extends NumericType | EnumType
? number
export type Primitive<T extends DType = DType> = T extends NumericLikeType
? number | (T extends BooleanType ? boolean : never) // let providers choose how to return booleans
: T extends StringType
? string
: T extends BooleanType
? number | boolean // let providers choose
: T extends ComplexType
? H5WebComplex
: T extends CompoundType<infer TFields>
? Primitive<TFields>[]
: unknown;

export type ArrayValue<T extends DType> =
| Primitive<T>[]
| (T extends NumericLikeType ? TypedArray : never);
: T extends ComplexType
? H5WebComplex
: T extends CompoundType<infer TFields>
? Primitive<TFields>[]
: unknown;

export type ArrayValue<T extends DType = DType> = T extends NumericLikeType
? TypedArray | number[] | (T extends BooleanType ? boolean[] : never) // don't use `Primitive` to avoid `(number | boolean)[]`
: Primitive<T>[];

export type Value<D extends Dataset> =
D extends Dataset<infer S, infer T>
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/src/mock-values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { range } from 'd3-array';
import ndarray, { type NdArray } from 'ndarray';

import { type ArrayValue, type DType, type H5WebComplex } from './hdf5-models';
import { type ArrayValue, type H5WebComplex } from './hdf5-models';
import { cplx } from './hdf5-utils';

const range1 = () => range(-20, 21);
Expand Down Expand Up @@ -276,4 +276,4 @@ export const mockValues = {
ndarray(range1().map((val) => Math.cos((val * 3.14) / 40))),
Y_scatter: () =>
ndarray(range1().map((_, i) => ((i % 10) + (i % 5)) / 123_456)),
} satisfies Record<string, () => NdArray<ArrayValue<DType>>>;
} satisfies Record<string, () => NdArray<ArrayValue>>;
5 changes: 3 additions & 2 deletions packages/shared/src/vis-utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { format } from 'd3-format';
import ndarray, { type NdArray, type TypedArray } from 'ndarray';
import ndarray, { type NdArray } from 'ndarray';
import { assign } from 'ndarray-ops';

import { assertLength, isNdArray } from './guards';
import {
type ArrayValue,
type BooleanType,
type ComplexType,
type EnumType,
Expand Down Expand Up @@ -101,7 +102,7 @@ export function toTypedNdArray<T extends TypedArrayConstructor>(
return ndarray(Constructor.from(arr.data) as InstanceType<T>, arr.shape);
}

export function createArrayFromView<T extends TypedArray | unknown[]>(
export function createArrayFromView<T extends ArrayValue>(
view: NdArray<T>,
): NdArray<T> {
const { data, size, shape } = view;
Expand Down

0 comments on commit 60462e7

Please sign in to comment.