Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Operator to cast value into literal #26180

Closed
FrankSandqvist opened this issue Aug 3, 2018 · 7 comments
Closed

Operator to cast value into literal #26180

FrankSandqvist opened this issue Aug 3, 2018 · 7 comments
Labels
Duplicate An existing issue was already created

Comments

@FrankSandqvist
Copy link

As far as I know, something like this does not exist already in Typescript.

I have a situation like this:

const mathBasicSetup = {
    inputs: {
        a: {
            dataType: {
                variant: 'STRING' as 'STRING',
                list: true as true,
                nullable: false as false
            }
        },
        b: {
            dataType: {
                variant: 'NUMBER' as 'NUMBER',
                list: false as false,
                nullable: true as true
            }
        },
    },
    outputs: {
        c: {
            slug: 'c',
            dataType: {
                variant: 'NUMBER' as 'NUMBER',
                list: false as false,
                nullable: false as false
            }
        },
    },
}

I define functions that are hard coded into my application. However I also want to be able to serve them up and their properties for my end users.

So I want to kill two birds in one stone and have type safety and the actual literal set in one variable.

I do this through typeof

export interface OperationBlock<S extends OperationBlockSetup> {
    setup: S;
    func: OperationBlockFunc<S>
}

const mathBasicFunc: OperationBlockFunc<typeof mathBasicSetup> = (inputs) => {
    // my inputs
    // typeof inputs.a = string
    // typeof inputs.b = string
}

In OperationBlockFunc I do this with the inputs

        inputs: { [P in keyof I]:
            I[P]['dataType']['variant'] extends keyof DataTypeVariantsInJS ?
            (
                I[P]['dataType']['nullable'] extends true ? (
                    I[P]['dataType']['list'] extends true ?
                    Array<DataTypeVariantsInJS[I[P]['dataType']['variant']]> | undefined :
                    DataTypeVariantsInJS[I[P]['dataType']['variant']] | undefined
                ) :
.....

...in order to cast the arguments to the correct type.

What I would love is some sort of an operator that would cast values into their literal counterparts.

e.g.

const s = 'mystring'
typeof s: string

const s = 'mystring' as 'mystring' 
typeof s: 'mystring

const s = §'mystring'
typeof s: 'mystring

const b = false
typeof b: boolean

const b = false as false
typeof b: false

const b = §false
typeof b = false

Essentially just a shorthand for foo as foo

@FrankSandqvist
Copy link
Author

Or perhaps a `literalof´keyword?

Don't know if it's the correct terminology for the ´false´, ´true´ and ´undefined´ etc.. type, though.

@MartinJohns
Copy link
Contributor

You can trivially do this with an own function:

function asLiteral<T extends string | number | boolean>(value: T): T { return value; }

const s = asLiteral('test'); // type: 'test'
const n = asLiteral(54); // type: 54
const b = asLiteral(true); // type: true

Not as nice as compile-time support and adds minimal negligible runtime overhead, but otherwise works fine.

@FrankSandqvist
Copy link
Author

FrankSandqvist commented Aug 3, 2018

Aha, I see!

Never thought about using a function. Thanks.

I still have some never issues using that, it doesn't seem to exactly slot into a 'foo' as 'foo', even though it seems it should..

I'll have to investigate.

Gotta love metaprogramming ¯_(ツ)_/¯

@FrankSandqvist
Copy link
Author

FrankSandqvist commented Aug 3, 2018

What would be even better is somehow being able to get the literal result when doing typeof on declared values.

const myObj = { bool: false, string: 'foobar' }

typeof myObj: { bool: boolean, string: string }

"literalof" myObj: { bool: false, string: "foobar" }

@MartinJohns
Copy link
Contributor

MartinJohns commented Aug 3, 2018

You can do the reverse, the "widening", already using conditional types:

type WidenedLiteral<T> =
    T extends string ? string :
    T extends number ? number :
    T extends boolean ? boolean :
    T;

type SW = WidenedLiteral<typeof s>; // Type is string
type NW = WidenedLiteral<typeof n>; // Type is number
type BW = WidenedLiteral<typeof b>; // Type is boolean

And with a mapped type you can change all literal types of an interface to their widened versions.

@RyanCavanaugh
Copy link
Member

Duplicate #10195 ?

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Aug 3, 2018
@FrankSandqvist
Copy link
Author

@RyanCavanaugh Looks like it! I'll close this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants