0

Typescript has string literals which describe the valid string values for a type (see docs). My issue is that I can't get it to work when using plain JavaScript objects which it seemed to do in the past.

For example I would like to type the valid currencies for a product in a shopping basket.

interface Product {
  title: string;
  description: string;
  price: number;
  quantity: number;
  currency: '$' | '£';
}

If I use a plain JavaScript object, let's say from a JSON source or JS file like this -

{
  title: 'Y Lift Neck Serum',
  description: 'This serum contains...',
  price: 400,
  currency: '$',
  quantity: 1
}

I get the error - Types of property 'currency' are incompatible. Type 'string' is not assignable to type '"$" | "£"'.ts(2322) "typescript": "~3.7.2". This is because string literals are a subset of string and not a string.

The solutiouns I've found are to cast or use const but I'm wondering if there is a type definition for currency that would "just work". I would rather not have to add const or as on multiple lines especially when the source file could be plain JavaScript or a server response.

What I also don't understand is how in another project this worked:

// "typescript": "^3.5.3" 
export type Route = {
    path: string;
    method: '*' | 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
    vhost?: string;
    handler: (request, reply) => any;
    options: object;
    rules: object;
};

Update: I created a code demo https://codesandbox.io/s/summer-rgb-5ruv7 and the error does not appear there. But I can still see it in VS code - maybe its a local config issue?

Type 'string' is not assignable to type '"$" | "£"' sample product product type

Typescript config

{ // tsconfig.base.json
  "compilerOptions": {
    "target": "es5",
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": false,
    "noImplicitReturns": true,
    "jsx": "preserve"
  },
  "exclude": ["node_modules", "build", "!node_modules/@types"]
}


{ // tsconfig.json
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowSyntheticDefaultImports": true,
    "isolatedModules": true,
    "jsx": "react"
  },
  "include": ["src"]
}
Peter
  • 4,493
  • 6
  • 41
  • 64
  • Your code seem to work fine. You can check here: https://www.typescriptlang.org/play/# – Rajesh Jan 10 '20 at 09:47
  • You need to say its some specific type and it is ok, `const a: Product = {... your object}` – Maciej Sikora Jan 10 '20 at 09:48
  • 1
    TS cannot just understand the relation between some object and your type without saying that it is exactly this type. TS will create a type from this object with quite wide definition, in order to avoid that you need to statically say the type – Maciej Sikora Jan 10 '20 at 09:49
  • https://www.typescriptlang.org/play/index.html?ssl=15&ssc=2&pln=9&pc=1# – Maciej Sikora Jan 10 '20 at 09:51
  • Thanks all. I tried to create a demo of my bug at https://codesandbox.io/s/summer-rgb-5ruv7 but I'm not able to reproduce it in the sandox(es) but I can see it in my VS code. :/ – Peter Jan 10 '20 at 10:52
  • @Peter Have you figured it out by any chance? – Zdenek F Dec 15 '21 at 20:13
  • @ZdenekF no i haven't :( i still have to use "const" or "as" for string literals – Peter Jan 08 '22 at 00:28

0 Answers0