Typescript: String not assignable to string enum even when value is known at compile time and is part of the enum.

Created on 28 Feb 2017  路  6Comments  路  Source: microsoft/TypeScript

[email protected]

Here's an example:

interface Test {
    fun(opts: {foo: 'enum1' | 'enum2'}): void
}

const bar: Test = {
    fun: (opts) => { }
}

const defaults = {
    foo: 'enum1'
};

bar.fun(defaults) // Error: Type 'string' is not assignable to type '"enum1" | "enum2"'

Shouldn't defaults.foo be inferred to be a subset of the string enum?

Question

Most helpful comment

Going to leave the answer to my case here because google brought me to this issue:

import * as isomorphicFetch from "isomorphic-fetch";
const fetchOptions: isomorphicFetch.RequestInit = {
    method: "POST",
    body: JSON.stringify(body),
    headers: headers,
    // required for cookies in ajax
    "credentials": "include",
};
let response = await isomorphicFetch(url, fetchOptions);

Caused this error:

file: 'file:///Users/backend/packages/nuxt-typescript/lib/api.ts'
severity: 'Error'
message: 'Argument of type '{ method: string; headers: { "Content-Type": string; }; "credentials": string; }' is not assignable to parameter of type 'RequestInit | undefined'.
  Type '{ method: string; headers: { "Content-Type": string; }; "credentials": string; }' is not assignable to type 'RequestInit'.
    Types of property 'credentials' are incompatible.
      Type 'string' is not assignable to type '"include" | "omit" | "same-origin" | undefined'.'
at: '120,51'
source: 'ts'

Solved by:

let fetchOptions: RequestInit = {

All 6 comments

FWIW, bar.fun({foo: 'enum1'}) compiles OK.

I would suggest defaults type to be inferred as { foo: string & 'enum1' } rather than string.

Note that defaults.foo is mutable, so the inferred type has to be string; otherwise it would prevent you from ever mutating it. Using & is even more dangerous as, if you _did_ mutate it, the & 'enum1' part of the type would make its type be 'enum1' even if it was any other value.

You probably want something similar to this, probably with an interface or type alias to avoid repetition:

const defaults: { foo: 'enum1' | 'enum2' } = {
  foo: 'enum1'
}

@Kovensky Right, hmm, yeah that does make sense. I'll close this. Thanks!

Going to leave the answer to my case here because google brought me to this issue:

import * as isomorphicFetch from "isomorphic-fetch";
const fetchOptions: isomorphicFetch.RequestInit = {
    method: "POST",
    body: JSON.stringify(body),
    headers: headers,
    // required for cookies in ajax
    "credentials": "include",
};
let response = await isomorphicFetch(url, fetchOptions);

Caused this error:

file: 'file:///Users/backend/packages/nuxt-typescript/lib/api.ts'
severity: 'Error'
message: 'Argument of type '{ method: string; headers: { "Content-Type": string; }; "credentials": string; }' is not assignable to parameter of type 'RequestInit | undefined'.
  Type '{ method: string; headers: { "Content-Type": string; }; "credentials": string; }' is not assignable to type 'RequestInit'.
    Types of property 'credentials' are incompatible.
      Type 'string' is not assignable to type '"include" | "omit" | "same-origin" | undefined'.'
at: '120,51'
source: 'ts'

Solved by:

let fetchOptions: RequestInit = {

It seems like tsc does not consider strings like "cors" or "default" to be of the types RequestMode or RequestCache, unless one adds the type declaration as @ubershmekel did in their answer.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DanielRosenwasser picture DanielRosenwasser  路  3Comments

seanzer picture seanzer  路  3Comments

jbondc picture jbondc  路  3Comments

bgrieder picture bgrieder  路  3Comments

siddjain picture siddjain  路  3Comments