DEV Community

mistlog
mistlog

Posted on

4 1

Create complex type using object spread

use typetype: https://github.com/mistlog/typetype

The url parser example:

export type function parseURL = (text) => ^{
    if (parseProtocol<text> extends [infer protocol, infer rest]) {
        return {
            protocol,
            ...parseAuthority<rest>
        }
    } else {
        return never
    }
}

type function parseProtocol = (text) => ^{
    if(text extends `${infer protocol}://${infer rest}`) {
        return [
            protocol,
            rest
        ]
    } else {
        return never
    }
}

type function parseUserInfo = (text) => ^{
    if(text extends `${infer username}:${infer password}`) {
        return { username, password }
    } else {
        return { username: text }
    }
}

type function parseAuthority = (text) => ^{
    if(text extends `${infer authority}@${infer rest}`) {
        return {
            authority: parseUserInfo<authority>,
            ...parseHost<rest>
        }
    } else { 
        return {
            authority: null,
            rest: text
        }
    }
}

type function parseHost = (text) => ^{
    if(text extends `${infer name}:${infer port}`) {
        return ^{
            if(parsePort<port> extends never) {
                return never
            } else {
                return { name, port }
            }
        }
    } else {
        return { name: text }
    }
}

type function parsePort = (text) => ^{
    if(isNumberString<text> extends true) {
        return text
    } else {
        return never
    }
}

type function isNumberString = (text) => ^{
    if(text extends "") {
        return never
    } else {
        return _isNumberString<text>
    }
}

type function _isNumberString = (text) => ^{
    /* the end of recursion: each char of text is digit, no more chars to inspect */
    if(text extends "") {
        return true
    } else if(text extends `${infer digit}${infer rest}`) {
        return ^{
            if(digit extends Digit) {
                return _isNumberString<rest>
            } else {
                return false
            }
        }
    } else {
        return false
    }
}

type Digit = union ["0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9"]
Enter fullscreen mode Exit fullscreen mode

Then, the generated type:

export type parseURL<text> = parseProtocol<text> extends [infer protocol, infer rest] ? object$assign<{}, [{
  protocol: protocol;
}, parseAuthority<rest>]> : never;
type parseProtocol<text> = text extends `${infer protocol}://${infer rest}` ? [protocol, rest] : never;
type parseUserInfo<text> = text extends `${infer username}:${infer password}` ? {
  username: username;
  password: password;
} : {
  username: text;
};
type parseAuthority<text> = text extends `${infer authority}@${infer rest}` ? object$assign<{}, [{
  authority: parseUserInfo<authority>;
}, parseHost<rest>]> : {
  authority: null;
  rest: text;
};
type parseHost<text> = text extends `${infer name}:${infer port}` ? parsePort<port> extends never ? never : {
  name: name;
  port: port;
} : {
  name: text;
};
type parsePort<text> = isNumberString<text> extends true ? text : never;
type isNumberString<text> = text extends "" ? never : _isNumberString<text>;
type _isNumberString<text> = text extends "" ? true : text extends `${infer digit}${infer rest}` ? digit extends Digit ? _isNumberString<rest> : false : false;
type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
Enter fullscreen mode Exit fullscreen mode

test it!

import { parseURL } from "./url-parser-2";

import { Test } from "ts-toolbelt"
const { checks, check } = Test

type url = `http://admin:123456@github.com:8080`;
type result = parseURL<url>

checks([
    check<result, {
        name: "github.com";
        port: "8080";
        authority: {
            username: "admin";
            password: "123456";
        };
        protocol: "http";
    }, Test.Pass>(),
])
Enter fullscreen mode Exit fullscreen mode

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up