# core/classifiers/schema.classifier.ts

> 54 lines of code and 11 definitions.

Tree: GovLab Patterns
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/patterns#file-patterns-core-classifiers-schema-classifier-ts
Source text: https://banes-lab.com/source/patterns/core/classifiers/schema.classifier.ts.txt

Listed in [core/classifiers](https://banes-lab.com/api/source/patterns/core/classifiers.md), before [core/classifiers/syntax.classifier.ts](https://banes-lab.com/source/patterns/core/classifiers/syntax.classifier.ts.md).

## Definitions

- `isBlank` (lexical_declaration, line 60, exported)
- `resolvePrimitive` (lexical_declaration, line 49, exported)
- `listKind` (lexical_declaration, line 14)
- `primitiveOf` (lexical_declaration, line 32, exported)
- `isFloatFamily` (lexical_declaration, line 42)
- `kindOf` (lexical_declaration, line 18, exported)
- `SCALAR_TYPES` (lexical_declaration, line 5)
- `DataLoadError` (class_declaration, line 7, exported)
- `constructor` (method_definition, line 8, exported)
- `type` (lexical_declaration, line 25, exported)
- `[only]` (lexical_declaration, line 53, exported)

## Contained in

- [core/classifiers](https://banes-lab.com/anatomy/patterns/folder-patterns-core-classifiers.md)

## Uses

- [configuration/strings/schema.strings.ts](https://banes-lab.com/source/patterns/configuration/strings/schema.strings.ts.md)
- [core/predicates/record.predicate.ts](https://banes-lab.com/source/patterns/core/predicates/record.predicate.ts.md)

## Used by

- [core/aggregators/field.aggregator.ts](https://banes-lab.com/source/patterns/core/aggregators/field.aggregator.ts.md)
- [core/validators/schema.validator.ts](https://banes-lab.com/source/patterns/core/validators/schema.validator.ts.md)

## Linked from

- [configuration/strings](https://banes-lab.com/anatomy/patterns/folder-patterns-configuration-strings.md)
- [core/aggregators](https://banes-lab.com/anatomy/patterns/folder-patterns-core-aggregators.md)
- [core/classifiers](https://banes-lab.com/anatomy/patterns/folder-patterns-core-classifiers.md)
- [core/predicates](https://banes-lab.com/anatomy/patterns/folder-patterns-core-predicates.md)
- [core/validators](https://banes-lab.com/anatomy/patterns/folder-patterns-core-validators.md)

## Source

```typescript
import type { Kind, Primitive } from "#types/schema.types";
import { mixedPrimitives, unsupportedValue } from "#configuration/strings/schema.strings";
import { isRecord } from "#core/predicates/record.predicate";

const SCALAR_TYPES: ReadonlySet<string> = new Set(["boolean", "number", "string"]);

export class DataLoadError extends Error {
    public constructor(message: string) {
        super(message);
        this.name = "DataLoadError";
    }
}

const listKind = function listKind(value: readonly unknown[]): Kind {
    return value.length > 0 && value.every((element) => isRecord(element)) ? "list-of-objects" : "list";
};

export const kindOf = function kindOf(value: unknown): Kind {
    if (Array.isArray(value)) {
        return listKind(value);
    }
    if (isRecord(value)) {
        return "object";
    }
    const type = typeof value;
    if (SCALAR_TYPES.has(type)) {
        return "scalar";
    }
    throw new DataLoadError(unsupportedValue(type));
};

export const primitiveOf = function primitiveOf(value: unknown, floatField: boolean): Primitive {
    if (typeof value === "boolean") {
        return "boolean";
    }
    if (typeof value === "number") {
        return floatField || !Number.isInteger(value) ? "float" : "integer";
    }
    return "string";
};

const isFloatFamily = function isFloatFamily(primitives: ReadonlySet<Primitive>): boolean {
    return (
        primitives.has("float") &&
        [...primitives].every((primitive) => primitive === "integer" || primitive === "float")
    );
};

export const resolvePrimitive = function resolvePrimitive(name: string, primitives: ReadonlySet<Primitive>): Primitive {
    if (isFloatFamily(primitives)) {
        return "float";
    }
    const [only] = [...primitives];
    if (primitives.size === 1 && only !== undefined) {
        return only;
    }
    throw new DataLoadError(mixedPrimitives(name, [...primitives].sort((a, b) => a.localeCompare(b)).join(", ")));
};

export const isBlank = function isBlank(value: unknown): boolean {
    return (value ?? null) === null;
};
```
