I'd like to define a type that will only allow nonempty binary strings. Something like:
type BinChar = "0" | "1"
type BinString = `${BinChar}${BinString}`
const b: BinString = "01" // should work
const a: BinString = "ab" // should fail
Unfortunately, this yields a "Type alias BinString circularly references itself" error.
Any other way to solve this?
I think that's impossible since you need to create all possible values with your type which like infinite, neither TS can infer generic types, you could use a factory function:
type BinString<T extends string, O = T>= T extends `${'0' | '1'}${infer A}` ? A extends '' ? O : BinString<A, O> : never;
function binaryString<T extends string>(arg: BinString<T>): T { return arg }
const bs = binaryString('01101010'); // ok
const bs2 = binaryString('003'); // error
const bs3 = binaryString(''); // error
Further improvements could be using flavored types, though you can reuse the check conditional type for functions' arguments:
type BinString = string & {_binaryString: true};
type IsBinString<T extends string, O = T>= T extends `${'0' | '1'}${infer A}` ? A extends '' ? O : IsBinString<A, O> : never;
function binaryString<T extends string>(arg: IsBinString<T>): BinString { return arg }
const bs = binaryString('01101010');
declare function binaryStringToNumber(str: BinString): number;
binaryStringToNumber(bs); // ok
binaryStringToNumber('01'); // error