import { expectAssignable, expectError, expectNotAssignable } from 'tsd' import { assert } from '../src/__tests__/utils' import spok, { Specifications } from './spok' // // Simple Object { const actual = { one: 1, two: 2, true: true, hello: 'hello', } type S = Specifications expectAssignable({ $topic: 'spok-test-valid', one: spok.ge(1), two: 2, }) expectAssignable({ one: spok.ge(1), two: spok.range(1, 2), }) expectNotAssignable({ one: 'string', two: 2, }) expectNotAssignable({ one: 1, twwo: 2, }) expectNotAssignable({ one: [1], two: 2, }) expectNotAssignable({ one: spok.ge(1), two: spok.range(1, 2), true: false, hello: Buffer.from('hello'), }) expectAssignable({}) expectNotAssignable(null) expectNotAssignable(void 0) } // // Array primitives // { const actual = [1, 2, 3] type S = Specifications expectAssignable([]) expectAssignable([2, 3, 4]) expectAssignable([1, 2, 3, 4]) expectAssignable([1]) expectAssignable([spok.number]) expectAssignable([spok.range(0, 2)]) expectNotAssignable({}) expectNotAssignable([{}]) expectNotAssignable(['string']) } // // Array objects // { const actual = [{ foo: 1, bar: 'ber' }, { shoes: 2 }] const actualConst = [{ foo: 1, bar: 'ber' }, { shoes: 2 }] as const type S = Specifications type SC = Specifications expectAssignable([]) expectAssignable([]) expectNotAssignable([1]) expectNotAssignable([1]) expectAssignable([{ foo: spok.number, bar: 'bar' }]) expectNotAssignable([{ foo: spok.number, bar: 'bar' }]) expectAssignable([{ foo: spok.number, bar: spok.endsWith('x') }]) expectAssignable([{ shoes: spok.number }]) expectAssignable([{ shoes: spok.number }]) expectAssignable([{ shoes: 2 }]) expectNotAssignable([{ shoes: 2 }]) expectAssignable([{ shoes: spok.range(1, 2) }]) } // // Mixed Arrays Props // { const actual = { numberStringArray: [1, 2, 'h', 'e'], } const actualConst = { numberStringArray: [1, 2, 'h', 'e'], } as const type S = Specifications type SC = Specifications expectAssignable({ numberStringArray: [spok.gtz, 2, spok.startsWith('l'), 'e'], }) expectAssignable({ numberStringArray: [spok.gtz, 2, spok.startsWith('l'), 'e'], }) expectAssignable({ numberStringArray: [spok.startsWith('h'), 'e'], }) // using `as const` enables checking types per slot // non-const allows any of the types in any slot expectNotAssignable({ numberStringArray: [spok.startsWith('h'), 'e'], }) } // // Nested Objects // { const actual = { country: { peru: { capital: 'lima', cities: { arequipa: { mountains: true, beach: 'nope', }, mancora: { mountains: false, beach: 'pretty', }, }, landscapes: [ { name: 'sierra', warm: false }, { name: 'selba', warm: true }, ], }, }, } const actualConst = { country: { peru: { capital: 'lima', cities: { arequipa: { mountains: true, beach: 'nope', }, mancora: { mountains: false, beach: 'pretty', }, }, landscapes: [ { name: 'sierra', warm: false }, { name: 'selba', warm: true }, ], }, }, } as const type S = Specifications type SC = Specifications expectAssignable({}) expectAssignable({}) expectAssignable({ country: {} }) expectAssignable({ country: {} }) expectNotAssignable({ land: {} }) expectNotAssignable({ land: {} }) expectAssignable({ country: { peru: {} } }) expectAssignable({ country: { peru: {} } }) expectNotAssignable({ country: { bolivia: {} } }) expectNotAssignable({ country: { bolivia: {} } }) expectAssignable({ country: { peru: { capital: spok.startsWith('l') } } }) expectAssignable({ country: { peru: { capital: spok.startsWith('l') } } }) expectAssignable({ country: { peru: { capital: 'no idea' } } }) expectNotAssignable({ country: { peru: { capital: 'no idea' } } }) expectAssignable({ country: { peru: { cities: {} } } }) expectAssignable({ country: { peru: { cities: {} } } }) expectNotAssignable({ country: { peru: { states: {} } } }) expectNotAssignable({ country: { peru: { states: {} } } }) expectAssignable({ country: { peru: { cities: { arequipa: {} } } } }) expectAssignable({ country: { peru: { cities: { mancora: {} } } } }) expectNotAssignable({ country: { peru: { cities: { medellin: {} } } } }) expectNotAssignable({ country: { peru: { cities: { bogota: {} } } } }) expectAssignable({ country: { peru: { cities: { arequipa: { mountains: false } } } }, }) expectNotAssignable({ country: { peru: { cities: { mancora: { mountains: true } } } }, }) expectNotAssignable({ country: { peru: { cities: { arequipa: { mountains: spok.le(2) } } } }, }) expectAssignable({ country: { peru: { cities: { arequipa: { beach: 'yes' } } } }, }) expectNotAssignable({ country: { peru: { cities: { arequipa: { beach: 'yes' } } } }, }) expectNotAssignable({ country: { peru: { cities: { arequipa: { beach: true } } } }, }) expectAssignable({ country: { peru: { landscapes: [] } }, }) expectNotAssignable({ country: { peru: { landscapes: true } }, }) expectAssignable({ country: { peru: { landscapes: [{ name: spok.string, warm: false }] } }, }) expectAssignable({ country: { peru: { landscapes: [{ name: spok.string, warm: false }] } }, }) expectAssignable({ country: { peru: { landscapes: [{ name: spok.string, warm: true }] } }, }) expectNotAssignable({ country: { peru: { landscapes: [{ name: spok.string, warm: true }] } }, }) expectAssignable({ country: { peru: { landscapes: [ { name: spok.string, warm: true }, { name: spok.startsWith('sel'), warm: spok.type('boolean') }, ], }, }, }) expectAssignable({ country: { peru: { landscapes: [ { name: spok.string, warm: false }, { name: spok.startsWith('sel'), warm: spok.type('boolean') }, ], }, }, }) expectAssignable({ country: { peru: { landscapes: [ { name: spok.string, warm: true }, { name: spok.startsWith('sel'), warm: spok.type('boolean') }, { name: spok.string }, ], }, }, }) expectNotAssignable({ country: { peru: { landscapes: [ { name: spok.string, warm: true }, { name: spok.startsWith('sel'), warm: spok.type('boolean') }, { name: spok.string }, ], }, }, }) } // // Types with optional properties // { type ColumnInfo = { column?: number } const actual: ColumnInfo = { column: 2, } type S = Specifications expectAssignable({ column: spok.ge(1), }) } // // Overriding spec type // { const actual = { foo: 1 } expectError(() => spok(assert, actual, { foo: '1' })) spok.any(assert, actual, { foo: '1' }) }