Skip to content

Commit

Permalink
feat(types): Writable
Browse files Browse the repository at this point in the history
Signed-off-by: Lexus Drumgold <[email protected]>
  • Loading branch information
unicornware committed Jul 19, 2023
1 parent 61b8750 commit 8e9f083
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 0 deletions.
241 changes: 241 additions & 0 deletions src/types/__tests__/writable.spec-d.ts
Original file line number Diff line number Diff line change
@@ -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<ReadonlyKeys<T>> extends true', () => {
// Arrange
type T = Vehicle

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})

it('should make all properties of T writable', () => {
// Arrange
type T = Readonly<Vehicle>

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<Vehicle>()
expectTypeOf<ReadonlyKeys<TestSubject<T>>>().toBeNever()
})
})

describe('T extends Primitive', () => {
describe('T extends NIL', () => {
it('should equal T', () => {
expectTypeOf<TestSubject<null>>().toEqualTypeOf<null>()
expectTypeOf<TestSubject<undefined>>().toEqualTypeOf<undefined>()
})
})

describe('T extends bigint', () => {
describe('IsLiteral<T> extends true', () => {
it('should equal T', () => {
// Arrange
type T = 0n

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})
})

describe('T extends object', () => {
it('should make all properties of T writable', () => {
// Arrange
type T = bigint & { readonly [opaque]: 'bigint' }

// Expect
expectTypeOf<ReadonlyKeys<TestSubject<T>>>().toBeNever()
})
})

describe('bigint extends T', () => {
it('should equal T', () => {
// Arrange
type T = bigint

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})
})
})

describe('T extends boolean', () => {
describe('IsLiteral<T> extends true', () => {
it('should equal T', () => {
// Arrange
type T = false

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})
})

describe('T extends object', () => {
it('should make all properties of T writable', () => {
// Arrange
type T = boolean & { readonly [opaque]: 'boolean' }

// Expect
expectTypeOf<ReadonlyKeys<TestSubject<T>>>().toBeNever()
})
})

describe('boolean extends T', () => {
it('should equal T', () => {
// Arrange
type T = boolean

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})
})
})

describe('T extends number', () => {
describe('IsLiteral<T> extends true', () => {
it('should equal T', () => {
// Arrange
type T = 0

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})
})

describe('T extends object', () => {
it('should make all properties of T writable', () => {
// Arrange
type T = number & { readonly [opaque]: 'number' }

// Expect
expectTypeOf<ReadonlyKeys<TestSubject<T>>>().toBeNever()
})
})

describe('number extends T', () => {
it('should equal T', () => {
// Arrange
type T = number

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})
})
})

describe('T extends string', () => {
describe('IsLiteral<T> extends true', () => {
it('should equal T', () => {
// Arrange
type T = 'vehicle'

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})
})

describe('T extends object', () => {
it('should make all properties of T writable', () => {
// Arrange
type T = string & { readonly [opaque]: 'string' }

// Expect
expectTypeOf<ReadonlyKeys<TestSubject<T>>>().toBeNever()
})
})

describe('string extends T', () => {
it('should equal T', () => {
// Arrange
type T = string

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})
})
})

describe('T extends symbol', () => {
describe('IsLiteral<T> extends true', () => {
it('should equal T', () => {
// Arrange
type T = typeof opaque

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})
})

describe('T extends object', () => {
it('should make all properties of T writable', () => {
// Arrange
type T = symbol & { readonly [opaque]: 'symbol' }

// Expect
expectTypeOf<ReadonlyKeys<TestSubject<T>>>().toBeNever()
})
})

describe('symbol extends T', () => {
it('should equal T', () => {
// Arrange
type T = symbol

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<T>()
})
})
})
})

describe('T extends Readonly<Fn>', () => {
it('should make all properties of T writable', () => {
expectTypeOf<ReadonlyKeys<TestSubject<Fn>>>().toBeNever()
expectTypeOf<ReadonlyKeys<TestSubject<Readonly<Fn>>>>().toBeNever()
})
})

describe('T extends readonly unknown[]', () => {
describe('IsTuple<T> extends true', () => {
it('should make indices of T writable', () => {
// Arrange
type T = readonly [Vehicle?]

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<[Vehicle?]>()
})
})

describe('number extends Length<T>', () => {
it('should make indices of T writable', () => {
// Arrange
type T = readonly Vehicle[]

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<Vehicle[]>()
})
})
})

describe('unions', () => {
it('should distribute over unions', () => {
// Arrange
type T = Nilable<Readonly<Vehicle> | readonly [Vehicle]>
type Expect = Nilable<Vehicle | [Vehicle]>

// Expect
expectTypeOf<TestSubject<T>>().toEqualTypeOf<Expect>()
})
})
})
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
19 changes: 19 additions & 0 deletions src/types/writable.ts
Original file line number Diff line number Diff line change
@@ -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> = T extends unknown
? { -readonly [K in keyof T]: T[K] }
: never

export type { Writable as default }

0 comments on commit 8e9f083

Please sign in to comment.