Typescript: Need to cast string as string literal

Created on 24 Jul 2018  路  15Comments  路  Source: microsoft/TypeScript


TypeScript Version: 3.1.0-dev.201xxxxx


Search Terms:
string as string, cast string as string, cast string as string literal
I am sorry, I have no idea on how to search for it, enlighten me, if you know.

Code

interface Iperiod {
    id: number | 'current';
    name: string;
}

function usePeriods(periods: Iperiod[]) {
    return periods;
}

usePeriods(['now', 'then'].map((name, index) => {
    if (name == 'now') {
        return {
            name,
            id: 'current',
        }
    }

    return {
        name,
        id: index,
    }
}));

Expected behavior:
It should work, as id can be number | 'current'.

Actual behavior:
Error string is not assignable to 'current'.

Playground Link:
link

Related Issues:
No, but I suppose a few.

Question

Most helpful comment

Note: 'something' as const is a bit less dumb but works the same.

All 15 comments

If you declares the return type of the lambda it works:

interface Iperiod {
    id: number | 'current';
    name: string;
}

function usePeriods(periods: Iperiod[]) {
    return periods;
}

usePeriods(['now', 'then'].map((name, index): Iperiod => {
    if (name == 'now') {
        return {
            name,
            id: 'current',
        }
    }

    return {
        name,
        id: index,
    }
}));

I think is a duplicate.

If I cast it like so it works:

return {
    name,
    id: 'current' as 'current',
}

, but damn, it feels dumb...

There are actually a few reasons why you should ensure an object literal has a contextual type -- it helps avoid misspelling optional properties and gets you literal types. (See palantir/tslint#2581)

@j-oliveras But the usePeriods function requires Iperiod[], therefor this information should be passed down to the map method return type, don't you think?

Created an issue to improve error reporting here: #26413

Could you please elaborate more on how this helps "avoid misspelling optional properties and gets you literal types"? Perhaps adding example, that says thousands words.
If this should be sometime closed it should bet a proper explanation of what is that big benefit that we have to cast string literal to string literals.

The "it" in that sentence reverse to "has a contextual type", which comes from giving a type annotation to the object. That also prevents you from needing to cast string literals.

interface I {
    kind: "a" | "b",
    optional?: number
}
declare function f(i: I): void;

// No contextual type
const i0 = { kind: "a" };
f(i0); // fails

const i1 = { kind: "a" as "a", optinal: 3 };
f(i1); // No compile error, but "optional" not specified as intended

f({ kind: "a", optional: 3 }); // Perfect!
f({ kind: "a", optinal: 3 }); // Compiler catches "optinal" spelling error

I still have hard time understanding it. Or at least to me it is almost like

f({ kind: "a", optinal: 3 });
f({ kind: "a" as "a", optinal: 3 });

I don't see why, if the string literal is in returned object is treated differently from one constructed in parameter.

@Akxe the effective question is, let's say you write this

const obj = { kind: "a" };
obj.kind = "b";

Is this code valid?

When you write an expression, TS has to guess what the domain you intended was. We use the same inference for determining how it's legal to use the object both for read and for writes -- obj.kind is inferred to be string unless specified otherwise, which means that the assignment of "b" is legal but that we aren't guaranteeing it's always "a" (thus the error).

I get that point, but consider this

interface I {
    code: 'mapped',
    name: string,
}

const a: I[] = ['a', 'b'].map(name => {
    return {
        code: 'mapped',
        name,
    }
});

Here the error is definitely unnecessary, as original will be always of type 'mapped'.

@Akxe That's a bug: #11152

It is 2 years old... Any info on what it is waiting for? I saw there waiting for proposal, but how to make one, is there a template?

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow.

'something' as 'something' seems verbose and pointless? TS should simply error if they didn't pass one of the string literals defined. I don't want my users to have to import a certain type they should just be able to pass a string that is type checked

Note: 'something' as const is a bit less dumb but works the same.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MartynasZilinskas picture MartynasZilinskas  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments

kyasbal-1994 picture kyasbal-1994  路  3Comments

bgrieder picture bgrieder  路  3Comments

zhuravlikjb picture zhuravlikjb  路  3Comments