Is it possible to make the 'keyof' property optional?

Illustrate an interface in the following way

interface Properties {
  apple?: string
  banana?: string
  cherry?: string
  date: string
}

Executing this code works as expected

type Sample1 = {
  [P in keyof Properties]: Properties[P]
}

const s1: Sample1 = {
  date: '1'
}

However, when I modify it like below, errors arise

type Keys = keyof Properties;

type Sample2 = {
  [P in Keys]: Properties[P]
}

// error Type '{ d: string; }' is missing the following properties from type 'Sample2': apple, banana, cherry(2739)

const s2: Sample2 = {
  date: '1'
}

Delving deeper

type CustomOmit<T, K extends keyof any> = { [P in Exclude<keyof T, K>]: T[P]; }

// Property 'apple' is missing in type '{ date: string; }' but required in type 'CustomOmit<Properties, "banana" | "cherry">'.
const o3: CustomOmit<Properties, 'banana' | 'cherry'> = {
  date: '1'
}

// No issues here!
const o4: Omit<Properties, 'banana' | 'cherry'> = {
  date: '1'
}

Why does CustomOmit trigger an error?

Answer №1

There's a significant distinction between

type Test1 = {  [P in keyof Props]: Props[P] }
and
type Test2 = { [P in Keys]: Props[P] }
.

Take a look at the first one: https://i.stack.imgur.com/J7dSW.png

In the first type, each property is optional.

Now, consider the second one: https://i.stack.imgur.com/TrYz3.png

In the second type, each property is required but may be undefined.

Refer to this flag for more information.

The reason TypeScript opted for this approach is due to the dynamic nature of JavaScript.

For instance:

const foo = { name: undefined }
foo.name // undefined
foo.surname // undefined

If you try to access a property that doesn't exist, JS returns undefined. Further details are available in the TypeScript docs.

Although this flag won't impact your example's behavior, it offers an official explanation for any errors encountered.

To resolve issues like these, you can simply make Test2 partial:

type Test2 = { [P in Keys]?: Props[P] } // <---- added question mark

Alternatively:

type Test2 = Partial<{ [P in Keys]: Props[P] }>

Why isn't keyof Props the same as Keys?

I overlooked this aspect while addressing the question.

It appears that in Test1, keyof Props directly iterates through the keys of the Props interface considering all modifiers, whereas in the iteration of Test2, TypeScript goes through a union of characters like a | b | c | d without knowledge about Props.

A similar scenario occurred when working with enums:


enum MyEnum {
    ONE,
    TWO
}

type Enumerate<Enum extends number | string> = keyof {
    [Prop in Enum]: Prop
}

type Result2 = Enumerate<MyEnum>

Enumerate should return the keys of MyEnum, which is a union of ONE | TWO, but it actually returns MyEnum

Answer №2

Here is where you can find the origin of Omit

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

So essentially, the difference lies in how keyof includes all keys as mandatory and creates an object, while Pick forms the object from the original T with optional declarations preserved.

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

What is the process of creating a typeorm relationship between orders and products?

My Orders Entity file in TypeOrm looks like this: @Entity('orders') export class OrdersEntity { @PrimaryGeneratedColumn('uuid') id: string; @CreateDateColumn() created: Date; @UpdateDateColumn() updated: Date; @Column('t ...

The code encountered an issue with TS2554 error, as it was anticipating two arguments but was only provided with one when

Before, I utilized ViewChild like this: @ViewChild("InternalMedia") localStream; @ViewChild("emoji") mEmoji; Everything was functioning well up until angular-7.x. However, after updating to angular-8.x, the following error started popping up: .../call_ ...

Definition of Stencil Component Method

I'm encountering an issue while developing a stencil.js web component. The error I'm facing is: (index):28 Uncaught TypeError: comp.hideDataPanel is not a function at HTMLDocument. ((index):28) My goal is to integrate my stencil component i ...

Leveraging both function arguments and the 'this' keyword within a single

I have a JavaScript function that utilizes both the `this` keyword and captures arguments: var Watcher = function() { var callbacks = []; var currentValue = null; this.watch = function (callback) { callbacks.push(callback); if (currentValue ...

Is there a way to utilize the 'interval' Rxjs function without triggering the Change Detection routine?

My goal is to display the live server time in my application. To achieve this, I created a component that utilizes the RXJS 'interval' function to update the time every second. However, this approach triggers the Change Detection routine every se ...

Angular2 and Typescript paired with Visual Studio 2013

Currently, I am utilizing the angular2 QUICKSTART and encountering an issue where Visual Studio fails to recognize Angular2 with typescript import Modules. However, everything else seems to be functioning correctly: https://i.stack.imgur.com/0s46Y.jpg Th ...

The element you are trying to access, "noUiSlider," does not belong to the type "HTMLElement" and cannot be found

Running into a roadblock here. What mistake am I making? .... /// <reference path="../../../typings/tsd.d.ts" /> var slider:HTMLElement = document.getElementById('slider'); noUiSlider.create(slider, { start: +$input.val(), step: + ...

What is the correct way to utilize Array.reduce with Typescript?

My current approach looks something like this: [].reduce<GenericType>( (acc, { value, status, time }) => { if (time) { return { ...acc, bestValue: valu ...

Guide to leveraging clsx within nested components in React

I am currently using clsx within a React application and encountering an issue with how to utilize it when dealing with mappings and nested components. For instance: return ( <div> <button onClick={doSomething}>{isOpened ? <Component ...

Error in Ionic Cordova Build prod: Module "." not found - Requires Typescript version >3

After updating my ionic project and all dependencies, I encountered an error when trying to build a --prod android apk: Uncaught Error: Cannot find module "." at vendor.js:1 at vendor.js:1 at Object.<anonymous> (vendor.js:1) at e (vendor.js:1) at Ob ...

Discovering how to specify the type of a dynamically created object using 'for await' in TypeScript

for await (user of users) { ... } Encountered an issue: "error TS2552: Cannot find name 'user'. Did you mean 'users'?" Appreciate your assistance. ...

Exploring TypeScript: Optional Sub-Properties

I've been exploring ways to create a type-alias with properties like "answer" that I came across in this post by another user (Typescript interface optional properties depending on other property). Here's an example: type Sample = { key1: true, ...

Vue component prop values are not properly recognized by Typescript

Below is a Vue component I have created for a generic sidebar that can be used multiple times with different data: <template> <div> <h5>{{ title }}</h5> <div v-for="prop of data" :key="prop.id"> ...

Error encountered in Angular/Ramda while using mapAccum with an array of objects in Typescript

I am having difficulties implementing Ramda in an Angular 6 / TypeScript environment. "ramda": "^0.25.0", "@types/ramda": "^0.25.24" This is how I have started: const addP = (p1,p2) => ({ x: p1.x+p2.x,y: p1.y+p2.y }); const accum = (a,b) => [add ...

Managing component composition in React/TypeScript: What's the best way to approach it?

I am brand new to the world of typescript, so please be patient with me. My objective is to transform this react component: interface ButtonProps {...} const Button: React.FC<ButtonProps> = ({ children, href, value as = 'button', ...

Refreshing the sub attributes of an incomplete entity

My Partial object contains sub-properties that may be undefined and need updating. interface Foo { data: string otherData: string } interface Bar { foo: Foo } interface Baz { bar: Bar } let a: Partial<Baz> = {} //... Goal: a.bar.foo ...

Encountering a compilation error while trying to utilize a union type in a function parameter within an

As stated on https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html, it is recommended to utilize multiple types for a parameter in a function (refer to the union part) /* OK */ interface Moment { utcOffset(): number; ...

What is the reason for encountering a TypeScript error when using a union type?

interface Bird { age:string, eat:()=>any } interface Fish { age:string, swim:()=>any } type Pet = Fish | Bird; everything looks good so far, defining a Pet type const pet:Pet={ age:"sd", eat:()=>{} } when trying to return ...

What is the process for creating a clickable file upload in Angular?

Currently, I am utilizing the instructions found in this guide to implement a file upload feature in my project. The code provided works perfectly: <input type="file" class="file-input (change)="onFileSelected($event)" #fileUplo ...

Has the antd Form.create() method been substituted with something else?

I have been working on creating a login page in react using antd. I came across a tutorial that seems to be outdated as it is giving me an error. After researching on a forum, I found out that form.create() is no longer available, but I am unsure of how to ...