From 8e9f083caf95314cfd253516334afbf43b556ab3 Mon Sep 17 00:00:00 2001 From: Lexus Drumgold Date: Tue, 18 Jul 2023 20:49:30 -0400 Subject: [PATCH] feat(types): `Writable` Signed-off-by: Lexus Drumgold --- src/types/__tests__/writable.spec-d.ts | 241 +++++++++++++++++++++++++ src/types/index.ts | 1 + src/types/writable.ts | 19 ++ 3 files changed, 261 insertions(+) create mode 100644 src/types/__tests__/writable.spec-d.ts create mode 100644 src/types/writable.ts diff --git a/src/types/__tests__/writable.spec-d.ts b/src/types/__tests__/writable.spec-d.ts new file mode 100644 index 00000000..e6500858 --- /dev/null +++ b/src/types/__tests__/writable.spec-d.ts @@ -0,0 +1,241 @@ +/** + * @file Type Tests - Writable + * @module tutils/types/tests/unit-d/Writable + */ + +import type Vehicle from '#fixtures/types/vehicle' +import type Fn from '../fn' +import type ReadonlyKeys from '../keys-readonly' +import type Nilable from '../nilable' +import type { tag as opaque } from '../opaque' +import type TestSubject from '../writable' + +describe('unit-d:types/Writable', () => { + describe('T extends ObjectCurly', () => { + it('should equal T if IsNever> extends true', () => { + // Arrange + type T = Vehicle + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + + it('should make all properties of T writable', () => { + // Arrange + type T = Readonly + + // Expect + expectTypeOf>().toEqualTypeOf() + expectTypeOf>>().toBeNever() + }) + }) + + describe('T extends Primitive', () => { + describe('T extends NIL', () => { + it('should equal T', () => { + expectTypeOf>().toEqualTypeOf() + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends bigint', () => { + describe('IsLiteral extends true', () => { + it('should equal T', () => { + // Arrange + type T = 0n + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends object', () => { + it('should make all properties of T writable', () => { + // Arrange + type T = bigint & { readonly [opaque]: 'bigint' } + + // Expect + expectTypeOf>>().toBeNever() + }) + }) + + describe('bigint extends T', () => { + it('should equal T', () => { + // Arrange + type T = bigint + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends boolean', () => { + describe('IsLiteral extends true', () => { + it('should equal T', () => { + // Arrange + type T = false + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends object', () => { + it('should make all properties of T writable', () => { + // Arrange + type T = boolean & { readonly [opaque]: 'boolean' } + + // Expect + expectTypeOf>>().toBeNever() + }) + }) + + describe('boolean extends T', () => { + it('should equal T', () => { + // Arrange + type T = boolean + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends number', () => { + describe('IsLiteral extends true', () => { + it('should equal T', () => { + // Arrange + type T = 0 + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends object', () => { + it('should make all properties of T writable', () => { + // Arrange + type T = number & { readonly [opaque]: 'number' } + + // Expect + expectTypeOf>>().toBeNever() + }) + }) + + describe('number extends T', () => { + it('should equal T', () => { + // Arrange + type T = number + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends string', () => { + describe('IsLiteral extends true', () => { + it('should equal T', () => { + // Arrange + type T = 'vehicle' + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends object', () => { + it('should make all properties of T writable', () => { + // Arrange + type T = string & { readonly [opaque]: 'string' } + + // Expect + expectTypeOf>>().toBeNever() + }) + }) + + describe('string extends T', () => { + it('should equal T', () => { + // Arrange + type T = string + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('T extends symbol', () => { + describe('IsLiteral extends true', () => { + it('should equal T', () => { + // Arrange + type T = typeof opaque + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + + describe('T extends object', () => { + it('should make all properties of T writable', () => { + // Arrange + type T = symbol & { readonly [opaque]: 'symbol' } + + // Expect + expectTypeOf>>().toBeNever() + }) + }) + + describe('symbol extends T', () => { + it('should equal T', () => { + // Arrange + type T = symbol + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + }) + + describe('T extends Readonly', () => { + it('should make all properties of T writable', () => { + expectTypeOf>>().toBeNever() + expectTypeOf>>>().toBeNever() + }) + }) + + describe('T extends readonly unknown[]', () => { + describe('IsTuple extends true', () => { + it('should make indices of T writable', () => { + // Arrange + type T = readonly [Vehicle?] + + // Expect + expectTypeOf>().toEqualTypeOf<[Vehicle?]>() + }) + }) + + describe('number extends Length', () => { + it('should make indices of T writable', () => { + // Arrange + type T = readonly Vehicle[] + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) + }) + + describe('unions', () => { + it('should distribute over unions', () => { + // Arrange + type T = Nilable | readonly [Vehicle]> + type Expect = Nilable + + // Expect + expectTypeOf>().toEqualTypeOf() + }) + }) +}) diff --git a/src/types/index.ts b/src/types/index.ts index 1f4282ed..8542e377 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -170,3 +170,4 @@ export type { default as UnionToTuple } from './union-to-tuple' export type { default as UnwrapNumeric } from './unwrap-numeric' export type { default as Values } from './values' export type { default as Whitespace } from './whitespace' +export type { default as Writable } from './writable' diff --git a/src/types/writable.ts b/src/types/writable.ts new file mode 100644 index 00000000..11cc845f --- /dev/null +++ b/src/types/writable.ts @@ -0,0 +1,19 @@ +/** + * @file Type Definitions - Writable + * @module tutils/types/Writable + */ + +/** + * Construct a type where all properties of `T` are writable. + * + * This is the opposite of {@linkcode Readonly}. + * + * @todo examples + * + * @template T - Type to evaluate + */ +type Writable = T extends unknown + ? { -readonly [K in keyof T]: T[K] } + : never + +export type { Writable as default }