Utilize TypeScript's TupleIndexed type to strictly enforce read-only properties for arrays when they are used as function arguments

Looking to define a TypeScript type that accepts a type parameter T along with a tuple or ReadonlyArray of keyof T, and returns a ReadonlyArray containing the keys indexed into T.

type TupleIndexed<T, K extends ReadonlyArray<keyof T>> = {
  [C in keyof K]: T[C];
};

Encountering the error message

Type 'C' cannot be used to index type 'T'
when trying this.

One workaround is as follows:

type TupleIndexed<T, K extends ReadonlyArray<keyof any>> = {
  [C in keyof K]: K[C] extends keyof T ? T[K[C]] : never;
};

Although this resolves the issue, it's not clear why the conditional statement is needed for the compiler to understand.

This approach allows for creating typed functions that maintain positional type information, illustrated below:

function pluck<T, K extends ReadonlyArray<keyof T>>(obj: T, keys: K): TupleIndexed<T, K> {
  return keys.map(key => obj[key]);
}

const vals = pluck({name: 'John', age: 25, adult: true}, [
  'name', 
  'age', 
  'adult'
] as const);
const name = vals[0]; // string
const age = vals[1]; // number
const adult = vals[2]; // boolean
const doesNotExist = vals[3]; // ERROR

However, omitting the array casting as const still compiles:

function pluck<T, K extends ReadonlyArray<keyof T>>(obj: T, keys: K): TupleIndexed<T, K> {
  return keys.map(key => obj[key]);
}

const vals = pluck({name: 'John', age: 25, adult: true}, [
  'name', 
  'age', 
  'adult'
]); // Compiles without explicit cast as const
const name = vals[0]; // string | number | boolean
const age = vals[1]; // string | number | boolean
const adult = vals[2]; // string | number | boolean
const doesNotExist = vals[3]; // string | number | boolean

This leads to loss of positional type safety. Is there a way to automatically enforce the array to be casted as const, or trigger an error when it's not explicitly done?

Answer №1

Is it possible to automatically convert the array into a const or trigger a compilation error if it is not converted into a const?

To my knowledge, there is no direct way to achieve this.

However, using a rest parameter instead of an array in TypeScript will treat it as a tuple rather than an array. This does require a slightly different syntax when calling it:

type TupleIndexed<T, K extends ReadonlyArray<keyof any>> = {
  [C in keyof K]: K[C] extends keyof T ? T[K[C]] : never;
};

function pluck<T, K extends ReadonlyArray<keyof T>>(obj: T, ...keys: K): TupleIndexed<T, K> {
//                                                          ^^^
  return keys.map(key => obj[key]) as any;
}

const vals = pluck({name: 'John', age: 25, adult: true}, 'name', 'age', 'adult');
const name = vals[0]; // string
const age = vals[1]; // number
const adult = vals[2]; // boolean
const doesNotExist = vals[3]; // error

Playground

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

Leveraging Ionic 2 with Moment JS for Enhanced TimeZones

I am currently working on integrating moment.js with typescript. I have executed the following commands: npm install moment-timezone --save npm install @types/moment @types/moment-timezone --save However, when I use the formattime function, it appears th ...

What is the best way to create a straightforward interface using TypeScript?

Although I'm sure this question has been asked before, I couldn't find the answer on Google or SO. So here it goes: I am looking to create an interface with a key named id of type number. Additionally, there may be other keys with unknown names ...

Using a try block inside another try block to handle various errors is a common practice in JavaScript

In an effort to efficiently debug my code and identify the location of errors, I have implemented a try-catch within a try block. Here is a snippet of the code: for (const searchUrl of savedSearchUrls) { console.log("here"); // function will get ...

"Firebase function fails to return Typescript class variable, resulting in 'undefined'

Being someone with a background in python/golang, I am now delving into ionic2. There seems to be an issue that I can't quite figure out due to my current level of knowledge in this stack. Perhaps I just need a way to reference the outer scope of this ...

The 'cookies' property is not found on the 'Request' type

Currently, I am attempting to access a cookie within a NestJS controller. I have been referencing the documentation found at https://docs.nestjs.com/techniques/cookies#use-with-express-default Below is my implementation: import { Controller, Get, Render, ...

Asynchronous jQuery operations using promises and finally functionality

I am attempting to interact with a REST api using jQuery's ajax feature. My goal is to return the Promise<Customer> object, but I am encountering an error stating that the property finally is missing. It used to work before, so I assume there h ...

Converting a string into a TypeScript class identifier

Currently, I am dynamically generating typescript code and facing an issue with quotes in my output: let data = { path: 'home', component: '${homeComponentName}', children:[] }; let homeComponentName = 'HomeComponent' ...

The 'setState' property is not found on the 'Window' type

I am encountering an issue with the code snippet in my index.tsx file let state = {}; window.setState = (changes: any) => { state = Object.assign({}, state, changes); ReactDOM.render(<App {...state} />, document.getElementById("root")); ...

How is it possible that there is no type error when utilizing copy with spread syntax?

When I use the map function to make a copy of an array of objects, why doesn't it throw an error when adding a new property "xxx"? This new property "xxx" is not declared in the interface. interface A{ a:number; b:string; }; let originalArray:A[] ...

Exploring the concept of abstract method generation in TypeScript within the Visual Studio Code

Anyone familiar with a Visual Studio Code plugin that can automatically generate stub implementations for abstract methods and properties in TypeScript? I've searched through the available plugins but haven't been able to locate one. Any suggest ...

Is there a more efficient method for providing hooks to children in React when using TypeScript?

My component structure looks something like this: Modal ModalTitle ModalBody FormElements MySelect MyTextField MyCheckbox DisplayInfo ModalActions I have a state variable called formVars, and a function named handleAction, ...

Encountering path import errors when developing a sample webpack site within a TypeScript library

Struggling to integrate my custom library with TypeScript and Webpack. Import errors are causing headaches, despite smooth sailing in CLion. Running tsc within the project directory is error-free, unlike when running npm run dev in the examples/webpack di ...

Each property of an object has its own unique key, yet they all share the same data type

I have a single-use object with only three properties, all of which should be of the same type. The code below currently achieves this, but I'm curious if there is a more efficient way to declare the type for timingsObject: let timingsObject: ...

Puppeteer with Typescript: Encountering issues during the transpilation process

The issue stems from the fact that I am unable to use Javascript directly due to Firebase Functions Node.JS version lacking support for Async/Await. As a workaround, I have converted the code into Typescript and am currently attempting to transpile it to c ...

Issue with ngRX infinite loop caused by the updateOne function in the adapter

Hey there, I'm struggling to figure out why my code is stuck in an infinite loop. I've searched online extensively but haven't found a solution that fits my specific issue. This is the code snippet causing the problem: /** * CODE ...

The exclude option in Nest JS middleware does not prevent the middleware from running on excluded routes

I'm having an issue with excluding certain routes from the middleware. The .exclude option doesn't seem to be working as expected, as the middleware is still being applied to the excluded routes. Here is the code for the Middleware: https://i.st ...

What is the syntax for creating a function with parameters of type `any` or `void` in TypeScript?

How can I create a function in typescript that accepts either something or nothing as input? I attempted the following: interface TestFn { (input: any | void): string } const operation: TestFn = (input) => 'result'; operation('some ...

What is the solution to the error message "Uncaught TypeError: createTheme_default is not a function"?

While working on my react application with vite, typescript, and mui, I encountered the following error: enter image description here This issue seems to be connected to material ui. Sometimes, deleting the 'deps' folder in '\node_mod ...

A versatile function catered to handling two distinct interface types within Typescript

Currently, I am developing a React application using TypeScript. In this project, I have implemented two useState objects to indicate if an addon or accessory has been removed from a product for visual purposes. It is important to note that products in thi ...

Building a TTL based schema in NestJs with MongooseIn this guide, we will explore

In my NestJs(TypeScript) project, I am attempting to create a self-destructing schema using the mangoose and @nestjs/mongoose libraries. Unfortunately, I have been unable to find a clear way to implement this feature. While I know how to do it in an expres ...