question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Symbol type is incorrectly generalized when used as a property value of an object literal

See original GitHub issue

TypeScript Version: 3.7.5

Search Terms: symbol infer type

Expected behavior:

Symbol const uniqueSymbol = Symbol() when used as property value in an object literal is inferred as unique type typeof uniqueSymbol.

Actual behavior:

Symbol const uniqueSymbol = Symbol() when used as property value in an object literal is inferred as general type symbol.

Related Issues:

Code

const uniqueSymbol = Symbol()

// Example of incorrect inference (unique symbol is generalized to type `symbol`)

const foo = {
  prop: uniqueSymbol,
}

type Foo = typeof foo  // { prop: symbol }

// Workarounds for incorrect behavior

const bar = {
  prop: uniqueSymbol as typeof uniqueSymbol,
}

type Bar = typeof bar // { prop: typeof uniqueSymbol }

const genericFactory = <T>(value: T) => ({ prop: value })

const baz = genericFactory(uniqueSymbol)

type Baz = typeof baz // { prop: typeof uniqueSymbol }

Output
"use strict";
const uniqueSymbol = Symbol();
// Example of incorrect inference (unique symbol is generalized to type `symbol`)
const foo = {
    prop: uniqueSymbol,
};
// Workarounds for incorrect behavior
const bar = {
    prop: uniqueSymbol,
};
const genericFactory = (value) => ({ prop: value });
const baz = genericFactory(uniqueSymbol);

Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "useDefineForClassFields": false,
    "alwaysStrict": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "downlevelIteration": false,
    "noEmitHelpers": false,
    "noLib": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "esModuleInterop": true,
    "preserveConstEnums": false,
    "removeComments": false,
    "skipLibCheck": false,
    "checkJs": false,
    "allowJs": false,
    "declaration": true,
    "experimentalDecorators": false,
    "emitDecoratorMetadata": false,
    "target": "ES2017",
    "module": "ESNext"
  }
}

Playground Link: Provided

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
weswighamcommented, Feb 20, 2020

Same is true of other literal types:

const uniqueSymbol = "tag"

const foo = {
  prop: uniqueSymbol,
}

type Foo = typeof foo 

prop is a mutable object member. Assigning a singleton type to a mutable member is usually not useful, so we widen at mutable positions. Do note, there is no way to declare an immutable object property (only class fields).

0reactions
pmellercommented, Feb 20, 2020

It seems that main issue that I’ve described works consistently for primitive types but at the same time TS as a language lacks other mechanisms for type symbol that allow to avoid generalization of variable that is a specific symbol. Please take a look at following techniques:

const literalString0 = 'literalString1'
const literalNumber0 = 1
const literalBoolean0 = true

// literal types are widened
const obj0 = {
  literalString0,
  literalNumber0,
  literalBoolean0,
}

// literal types are preserved

// technique #1 using `as const`
const literalString1 = 'literalString1' as const
const literalNumber1 = 1 as const
const literalBoolean1 = true as const

const obj1 = {
  literalString1,
  literalNumber1,
  literalBoolean1,
}

// technique #2 using explicit type declaration
const literalString2: 'literalString1' = 'literalString1'
const literalNumber2: 1 = 1
const literalBoolean2: true = true

const obj2 = {
  literalString2,
  literalNumber2,
  literalBoolean2,
}

Playground link

Both as const casting and explicit type declaration cannot be used on symbols.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Symbol type - The Modern JavaScript Tutorial
A “symbol” represents a unique identifier. A value of this type can be created using Symbol() : let id = Symbol();.
Read more >
TypeError: can't assign to property "x" on "y": not an object
In strict mode, a TypeError is raised when attempting to create a property on primitive value such as a symbol, a string, a...
Read more >
Why am I getting an error "Object literal may only specify ...
Usually this error means you have a bug (typically a typo) in your code, or in the definition file. The right fix in...
Read more >
14. New OOP features besides classes - Exploring JS
However, it also includes new features for object literals and new utility ... One use case for property value shorthands are multiple return...
Read more >
You Don't Know JS: this & Object Prototypes - GitHub Pages
Type. Objects are the general building block upon which much of JS is built. ... If you use any other value besides a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found