Skip to content

Commit

Permalink
fix: correct inference of arrays of branded literal types (#85)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: This changes the meaning of the type parameter of the `array` function.
  • Loading branch information
pavadeli authored Oct 26, 2023
1 parent 56184f3 commit e6521cf
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 14 deletions.
2 changes: 1 addition & 1 deletion etc/types.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
```ts

// @public
export function array<Element>(...args: [name: string, elementType: BaseTypeImpl<Element>, typeConfig?: ArrayTypeConfig] | [elementType: BaseTypeImpl<Element>, typeConfig?: ArrayTypeConfig]): TypeImpl<ArrayType<BaseTypeImpl<Element>, Element, Element[]>>;
export function array<ElementType extends BaseTypeImpl<any>>(...args: [name: string, elementType: ElementType, typeConfig?: ArrayTypeConfig] | [elementType: ElementType, typeConfig?: ArrayTypeConfig]): TypeImpl<ArrayType<ElementType, TypeOf<ElementType>, Array<TypeOf<ElementType>>>>;

// @public
export class ArrayType<ElementType extends BaseTypeImpl<Element>, Element, ResultType extends Element[]> extends BaseTypeImpl<ResultType, ArrayTypeConfig> {
Expand Down
16 changes: 8 additions & 8 deletions markdown/types.array.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ Create a type that checks whether the input is an array and all elements conform
**Signature:**

```typescript
declare function array<Element>(
declare function array<ElementType extends BaseTypeImpl<any>>(
...args:
| [name: string, elementType: BaseTypeImpl<Element>, typeConfig?: ArrayTypeConfig]
| [elementType: BaseTypeImpl<Element>, typeConfig?: ArrayTypeConfig]
): TypeImpl<ArrayType<BaseTypeImpl<Element>, Element, Element[]>>;
| [name: string, elementType: ElementType, typeConfig?: ArrayTypeConfig]
| [elementType: ElementType, typeConfig?: ArrayTypeConfig]
): TypeImpl<ArrayType<ElementType, TypeOf<ElementType>, Array<TypeOf<ElementType>>>>;
```

## Parameters

| Parameter | Type | Description |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------ |
| args | \[name: string, elementType: [BaseTypeImpl](./types.basetypeimpl.md)<!-- -->&lt;Element&gt;, typeConfig?: [ArrayTypeConfig](./types.arraytypeconfig.md)<!-- -->\] \| \[elementType: [BaseTypeImpl](./types.basetypeimpl.md)<!-- -->&lt;Element&gt;, typeConfig?: [ArrayTypeConfig](./types.arraytypeconfig.md)<!-- -->\] | optional name and element-type |
| Parameter | Type | Description |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------ |
| args | \[name: string, elementType: ElementType, typeConfig?: [ArrayTypeConfig](./types.arraytypeconfig.md)<!-- -->\] \| \[elementType: ElementType, typeConfig?: [ArrayTypeConfig](./types.arraytypeconfig.md)<!-- -->\] | optional name and element-type |

**Returns:**

[TypeImpl](./types.typeimpl.md)<!-- -->&lt;[ArrayType](./types.arraytype.md)<!-- -->&lt;[BaseTypeImpl](./types.basetypeimpl.md)<!-- -->&lt;Element&gt;, Element, Element\[\]&gt;&gt;
[TypeImpl](./types.typeimpl.md)<!-- -->&lt;[ArrayType](./types.arraytype.md)<!-- -->&lt;ElementType, [TypeOf](./types.typeof.md)<!-- -->&lt;ElementType&gt;, Array&lt;[TypeOf](./types.typeof.md)<!-- -->&lt;ElementType&gt;&gt;&gt;&gt;
14 changes: 14 additions & 0 deletions src/types/array.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,17 @@ testTypes(() => {
// @ts-expect-error wrong element type
assignableTo<MyArray>([1]);
});

testTypes('correct inference of arrays of branded types', () => {
type MyBrandedType = The<typeof MyBrandedType>;
const MyBrandedType = string.withConfig('MyBrandedType', { minLength: 2, maxLength: 2 });
type MyBrandedTypeArray = The<typeof MyBrandedTypeArray>;
const MyBrandedTypeArray = array(MyBrandedType);

const brandedArray = MyBrandedTypeArray.literal(['ab']);

assignableTo<MyBrandedType[]>(brandedArray);
assignableTo<MyBrandedTypeArray>([MyBrandedType.literal('ab')]);
// @ts-expect-error not a branded string
assignableTo<MyBrandedTypeArray>(['ab']);
});
10 changes: 5 additions & 5 deletions src/types/array.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BaseTypeImpl, createType } from '../base-type';
import type { ArrayTypeConfig, Result, TypeImpl, ValidationOptions, Visitor } from '../interfaces';
import type { ArrayTypeConfig, Result, TypeImpl, TypeOf, ValidationOptions, Visitor } from '../interfaces';
import {
castArray,
decodeOptionalName,
Expand Down Expand Up @@ -98,11 +98,11 @@ define(ArrayType, 'createAutoCastAllType', function (this: ArrayType<BaseTypeImp
*
* @param args - optional name and element-type
*/
export function array<Element>(
export function array<ElementType extends BaseTypeImpl<any>>(
...args:
| [name: string, elementType: BaseTypeImpl<Element>, typeConfig?: ArrayTypeConfig]
| [elementType: BaseTypeImpl<Element>, typeConfig?: ArrayTypeConfig]
): TypeImpl<ArrayType<BaseTypeImpl<Element>, Element, Element[]>> {
| [name: string, elementType: ElementType, typeConfig?: ArrayTypeConfig]
| [elementType: ElementType, typeConfig?: ArrayTypeConfig]
): TypeImpl<ArrayType<ElementType, TypeOf<ElementType>, Array<TypeOf<ElementType>>>> {
const [name, elementType, typeConfig] = decodeOptionalName(args);
return createType(new ArrayType(elementType, typeConfig ?? {}, name));
}
Expand Down

0 comments on commit e6521cf

Please sign in to comment.