Why is there an error when trying to assign Type '{}' in generics typescript?

I'm having trouble understanding why this code is causing an error. The error message says - Type '{}' is not assignable to type 'Keys<T>'.

type Keys<T extends string|symbol>={
    [key in T]: string;
};
const foo = <T extends string|symbol>()=>{
    const a:Keys<T> = {}
    return a
}

Interestingly, if you manually replace the string or symbol type, no errors occur. Instead, there's just a warning that T is declared for a function but not used.

Here are working examples of the code:

type Keys<T extends string|symbol>={
    [key in T]: string;
};
const foo = <T extends string|symbol>()=>{
    const a:Keys<string> = {}
    return a
}
type Keys<T extends string|symbol>={
    [key in T]: string;
};
const foo = <T extends string|symbol>()=>{
    const a:Keys<symbol> = {}
    return a
}

You can view the code here

I was expecting the generic code to work correctly.

Answer №1

In summary: Typescript is more lenient with wide property types like string than with more specific property types like 'a' | 'b' | 'c'.


To simplify, your Keys type is essentially the built-in Record type which is defined as:

type Record<K extends string | number | symbol, T> = { [P in K]: T; }

So, for simplicity's sake, let's use that instead.


But why does Typescript behave this way?

function foo<T extends string | symbol>() {
  const foo: Record<string, string> = {} // no issue
  const bar: Record<T, string> = {} // error
}

The reason lies in how Typescript handles keys that are either infinite or finite.

  • string can be any string value, so it's not strictly tracked.
  • 'a' | 'b' | 'c' represents a finite set of specific strings.

Typescript doesn't enforce the presence of infinite keys because it can't. It allows any string to be used as a key because the type definition permits it.

However, this leniency can lead to issues such as:

const obj: Record<string, string> = { a: 'test' }
obj.b.toUpperCase() // no type error, but may cause runtime crash

A more suitable type would be:

const obj: Record<string, string | undefined> = { a: 'test' }
obj.b.toUpperCase() // type error
obj.b?.toUpperCase() // okay

By allowing the value type to be undefined, we ensure that properties must have a value before being treated as a string, reinstating type safety.

When the compiler can determine the keys involved, it enforces stricter typing:

const obj: Record<'a', string> = { a: 'test' }
obj.b.toUpperCase() // type error

With more information available, Typescript applies stronger type checking in these cases.


Regarding this code snippet:

const foo = <T extends string|symbol>()=>{
    const a: Record<T, string> = {} // type error
    return a
}

Typescript assumes that T will likely be inferred as a finite subset of string | symbol, leading to stricter type checking.

However, in your code, no properties are assigned, although the types suggest otherwise:

foo<{ a: number }>().a // number

Since the property is never assigned, you'll encounter undefined at runtime, potentially causing issues elsewhere in your code.

Answer №2

Due to the fact that

let bar = <T = "c" | "d">()=>{
    const c: {c: string, d: string} = {} // <- not compatible
    return c
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How do I designate the compiled migration location?

Currently, I am utilizing the PostgreSQL database ORM Sequelize along with TypeScript as a backend script within the Express Node.js environment. My first inquiry is: Is it possible to directly create a model in .ts format? The second question pertains t ...

Add numerical identifiers to numerous camera entities

I am working on a three js scene that includes a 3D model, and I would like to incorporate multiple cameras into the model. In order to distinguish between each of the cameras in the scene, I am looking for a way to label them with numbers, possibly near ...

What is the best way to transfer data between functions?

I'm working on a fun Santa's game to play with my friends. The concept is simple: you enter your name, click a button, and a random name from the list will be displayed as the result. I've encountered a couple of challenges: I can succe ...

Unable to assign image src to a dynamically generated div

My current task involves setting the background URL of a dynamically created div in JavaScript, intended for use with the jQuery Orbit slider. Below is my approach: var content1 = null; $("#featured").html("<div id='content' style='&apos ...

Using Mongoose and MongoDB to reference a different schema without using the default ObjectId reference

I have two different schemas in my current project. var carSchema = new mongoose.Schema({ id: { type: Number, unique: true, required: true }, make: { type: String, required: true }, model: { ...

Utilizing Node.js to insert data into separate MongoDB schemas

data.js var dataSchema = Schema({ item : String, description : String, category : { type: Schema.Types.ObjectId, ref: 'Category' } }); var Data = mongoose.model('Data&a ...

What is the best way to recover accented characters in Express?

Encountering issues with accented characters in my POST request .. I attempted using iconv without success.. Snippet of my code: Edit: ... var bodyString = JSON.stringify(req.body); var options = { host: XXXXX, port: XXX, ...

What is the best way to customize a component in a view?

<template> <div class="about"> <Header /> <h1>Welcome to the dashboard page</h1> </div> </template> <script> import Header from "../components/layout/Header.vue"; export default { name: "dashb ...

Generate Table Rows with Javascript - produces an excess of cells...>>

I am currently in the process of dynamically creating a table, but I am facing an issue with the number of cells being added. The table I am trying to create has a total of 8 columns (Monday to Friday and a total column at the end). You can view my progr ...

What is the best way to ensure that my mat-slide-toggle only changes when a specific condition is met?

I'm having an issue with a function that toggles a mat-slide-toggle. I need to modify this function to only toggle when the result is false. Currently, it toggles every time, regardless of the result being true or false. I want it to not toggle when t ...

Using router.get with a redirect in Express

Can you directly invoke a router.get(...) function in Express? Let's say I have a router.get('/my-route', function(req, res) { ... });, is it feasible to then, within another part of my code, use res.redirect('my-route'); with the ...

What is preventing me from updating the value of a variable in this manner?

Trying to understand the reason behind an issue with overwriting a value passed to an angularJS directive using an isolate scope (@). When attempting to change the value of vm.index with the following: vm.index = parseInt(vm.index, 10) It does not seem t ...

What is the functionality of the disabled attribute on an option tag within a dropdown select menu?

I am working with a code snippet that involves understanding how the attribute:disabled functions on an <option> within a <select> element! Let's say I have a dropdown for ratings and I select the 5-star option (★★★★★). Upon sel ...

Building a webRTC peer using only a JavaScript interpreter, no browser required

My goal is to develop a WebRTC peer that functions solely as a listener/recorder without any graphical presentation like HTML/CSS involved. If achievable using the WebRTC JavaScript APIs, I am curious about which standalone JavaScript engine I should cons ...

Effortless login authentication using Disqus through Google or Facebook tokens

I have set up my website to allow users to authenticate through Google and Facebook using passport, which uses the OAuth 2.0 API. Additionally, I am utilizing Disqus to manage the comments system on my site. However, I am running into a problem where user ...

Unleashing the power of Angular 7+: Extracting data from a JSON array

As a newcomer to Angular and API integration, I am facing a challenge in fetching currency exchange rates data from the NBP Web API. The JSON file structure that I'm working with looks like: https://i.stack.imgur.com/kO0Cr.png After successfully ret ...

Uncovering a commitment to reveal the valuable information within

Whenever my Spring Boot back-end responds to front-end requests, it structures the data like this: { "timestamp":[2022,6,16], "status":"OK", "data": { "products": [{"product ...

How come this variable isn't recognized as 0 even though the debugger is indicating otherwise?

I am currently working on a React component that receives the total number of reviews as a prop. In cases where the number of reviews is 0, I intend to display an element indicating <div>No reviews yet.</div> If there are reviews available, I ...

Intellisense with JavaScript methods is not supported in Vue files

While running Vue 2.6 in VSCode, I've noticed that my intellisense functions perfectly within js files. However, as soon as I switch to a vue file, all my intellisense capabilities disappear. I have the most up-to-date version of VSCode installed and ...

Redux: streamlining containers, components, actions, and reducers for seamless organization

Here's the question: When working on a large React/Redux application, what is the most effective and sustainable method for organizing containers, components, actions, and reducers? My take: The current trend leans towards organizing redux elemen ...