/r/typescript

Photograph via snooOG

TypeScript is a language for application-scale JavaScript development.

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.

Resources

Language

Discussion

Rules

  1. Your post should be typescript focused This is r/typescript, lets keep it on topic
  2. No general advertising, promoting services/libs with zero TS utility or closed source projects We get it, you people build awesome things, but this isn't r/sideproject, if you're posting a project it needs to be open source, you need to link to the repo and most importantly given that this is r/typescript it should not only be in typescript but be something that contributes to TS utility (Not just a random lib that happens to be written in TS). Also no general spam of other products or software, even if it's free. Exceptions can be made for software that will be exceptionally useful for typescript development and pipelines, but this is at the moderation teams discretion.
  3. Keep self promotion to a minimum. "It's perfectly fine to be a redditor with a website, it's not okay to be a website with a reddit account." - Confucius
  4. No Recruiters. or recruitment posts, we have a sticky for that, still, it's for redditors only, not professional recruiters.

/r/typescript

146,309 Subscribers

7

Testing and Typescript

I've never written unit tests in a language like typescript before where the consumer of my code may themselves not be types, or may have a different strictness level of types, or may be wildly casting types as 'any'. Do you ever consider these cases in testing?

It seems almost ridiculous to ask, because if the answer is yes, then why not consider it in the function itself? Why not wrap every parameter to every function in type guards for safety, and at that point why even write in typescript? Let's not go down that road...

But that leaves the behavior undefined. For reference, I'm writing a library, and I have two identical functions. One is fast and dangerous, relying entirely on my type system to validate parameters and a bunch of casting happening inside. The other is slower but is littered with type guards. They each pass all the same test cases but the faster one is unpredictable if typescript is circumvented and bad parameters are passed; sometimes it will throw, sometimes it will return undefined, sometimes it will do the unexpected. Should I even care?

11 Comments
2025/02/02
01:35 UTC

9

Who's hiring Typescript developers February

The monthly thread for people to post openings at their companies.

* Please state the job location and include the keywords REMOTE, INTERNS and/or VISA when the corresponding sort of candidate is welcome. When remote work is not an option, include ONSITE.

* Please only post if you personally are part of the hiring company—no recruiting firms or job boards **Please report recruiters or job boards**.

* Only one post per company.

* If it isn't a household name, explain what your company does. Sell it.

* Please add the company email that applications should be sent to, or the companies application web form/job posting (needless to say this should be on the company website, not a third party site).

Commenters: please don't reply to job posts to complain about something. It's off topic here.

Readers: please only email if you are personally interested in the job.

Posting top level comments that aren't job postings, [that's a paddlin](https://i.imgur.com/FxMKfnY.jpg)

0 Comments
2025/02/01
00:00 UTC

2

What type should I use for a key value pair object of unknown structure?

I need to process key value pair objects from an api but I have no idea about their structure or how deep they are.

For example:

function processApiObject(x: object) {

if ( x.domain === "example" ) {

// Do something ...

}

}

I want to avoid using just object.

13 Comments
2025/01/31
18:46 UTC

14

Best way to use array.includes in typescript

I think it’s pretty common to have an array of a string union type. Like:

const a = [‘a’, ‘b’] as const;
Type U = (typeof a)[number];

Now, if I want to use this to check if some value is a U I’d like to do:

 a.includes(someString);

Of course, this gives an error because string isn’t assignable to the type of the array - but that’s exactly what I need it for. So far, I use this:

(a as string[]).includes(someString)

Which… meh, it’s ok, but it is really lying to the compiler. I know that a is of type U[], not string[].

Is there a good way to do this?

27 Comments
2025/01/31
09:39 UTC

18

I think it would be cool if the number type could include a range

Such that, for instance, an argument could be constrained to between zero and one, and if you tried to pass in a value that was outside of that range, you’d get a type error. There are some workarounds for similar behavior, but first class support would be neat

29 Comments
2025/01/31
07:48 UTC

2

Can you make an existing tuple type readonly?

I can do the following:

type Kid = [ string, string, number ]; type ReadonlyKid = readonly [ string, string, number ];  type KidTuple = { readonly [ K in keyof Kid ]: Kid[K] }; type KidTuple2 = { readonly [ K in keyof ReadonlyKid ]: ReadonlyKid[K] };  const testKid1: KidTuple = [ 'Noah', 'Ortiz', 7 ]; testKid1.pop();  const testKid2: KidTuple2 = [ 'Noah', 'Ortiz', 7 ]; testKid2.pop(); // <-- doesn't work as expected

The ReadonlyKid type makes it so that you're unable to pop or push elements in the tuple. However, I needed to create this ReadonlyKid type to make this work. If we only had access to the Kid type which is from some third party library, how would I create a similarly readonly tuple based on it?

I can't do the following instead:

type ReadonlyKid = readonly Kid;

because that gets flagged by typescript as only being allowed on array or literal tuple types.

Is there any way to accomplish this using the readonly keyword, not Readonly<T>?

p.s. I don't know why carriage returns are not working in the code-block.

3 Comments
2025/01/30
22:36 UTC

1

Which tsconfig settings impact import autocomplete from "references"?

Hi, i have a monorepo:

web (vite react app)
api (expressJs backend)
schema (shared local package, which is used by web and api)

The idea is: when builded, web and api must resolve import from @my-monorepo/schema to schema's dist/index.js, while during development i need it to point to src/ .ts file.

To achieve this i am trying to use project references https://www.typescriptlang.org/docs/handbook/project-references.html

The problem is:

During development web package resolves @my-monorepo/schema to /schema/DIST/types/index. And does not provide autocompletion. To make it work as i want i need to manually type @my-monorepo/schema/SRC But api works as expected @my-monorepo/schema resolved to /schema/src/index and autompletion works fine.

What tsconfig settings can affect this?

schema package.json has entry point to builded dist

  "main": "./dist/index.js",
  "module": "./dist/index.js",
  "types": "./dist/types/index.d.ts",

schema tsconfig.json:

"composite": true, // enabled for references
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"rootDir": "src",
"baseUrl": "./src",
"outDir": "dist",
"declarationDir": "dist/types",
...
"include": ["src/**/*"],
"exclude": ["dist"],
...

web and api tsconfigs both have references specified:

"references": [{ "path": "../schema" }]
2 Comments
2025/01/30
18:22 UTC

1

Help me understand `customConditions`

I've re-read it multiple times now and I know that it's a special config with a special use-case but I'm writing a full-fledged course and I would really love to include the explanation of this option.

I feel like my head is stuck on repeat, anybody able to explain it? https://www.typescriptlang.org/tsconfig/#customConditions

5 Comments
2025/01/30
12:48 UTC

4 Comments
2025/01/30
12:39 UTC

1

Is there documentation on errors?

I'm looking for an official documentation on each error where you can search for something like TSxxxx and you find an entry with information about the error, how to debug it, common gotchas etc.

Does that exist? Obviously most of us use Stack Overflow, but do people first learn about an error and why it occurs?

8 Comments
2025/01/30
09:18 UTC

0

Generic Type to String - is it possible?

Hey,

I want to use chatGPT as a sort of API that returns JSON and therefore want to tell it what type it should return as. It does that really well when using the UI. Something like: "Give me a list of 5 countries. Return JSON only in the format of { countries: string [] }.

For my implementation i want to pass that type from typescript into my prompt. Like so
"useGPT<{ countries: string[] }>('Give me a list of 5 countries')"

Is it possible to create a string from that Generic Type that I can pass into a string prompt?

Help is appreciated 🙏

6 Comments
2025/01/30
07:31 UTC

3

Looking for monorepo tech-stack feedback for an enjoyable dev experience

Hey everyone, I am software engineer who usually works in other languages, but have decided to pick up TS for my latest project. I am looking for recommendation on what technologies could I utilize. I hate these kind of questions, but to be completely honest, the whole ecosystem is so vast, it's very difficult to get a hold of things.

We have an expo app, and would like to create a monorepo, we need basic CRUD functionalities with support for AI Agents and LLM prompting.

Current ideas are:

  • RN/ Expo for mobile
  • NodeJS for backend, I've evaluated Bun and Deno but not sure it's worth going cutting edge and losing the vast ecosystem NodeJS provides.
  • Hono as the web framework, I would like to utilize it's tRPC like features, or just integrate with tRPC itself.
  • Drizzle for the database interactions
  • Inngest to decouple the codebase with events
  • InstructorJS for structured LLM prompting, or go with a framework like mastra.ai, not sure if there are any defacto options here, since I just need simple chaining, tool usage and structured outputs
  • Jest for testing, I guess it does it job?
  • pnpm for the package manager

Priorities are dev experience and testability, I'd expect decent performance from any framework in 2025. Simplicity and low mental overhead is always a plus.

What do you think? Any technologies that would better fit my usecase, or any that I've missed?

8 Comments
2025/01/30
06:57 UTC

0

How to debug "Database connection or query failed" when trying to connect to a Postgresql database?

I have a "working" script that connects to postgresql. This script works perfectly fine in another machine. However, I am trying to re-setup the project to another machine. Despite following almost the same step (some naming being different, but nothing major).

This is the db.ts

import { Pool } from 'pg';

import dotenv from 'dotenv';

// Load environment variables from the .env file in the src directory

const result = dotenv.config({ path: './src/.env' });

if (result.error) {

throw new Error('Failed to load .env file. Please ensure it exists in the src directory.');

}

console.log('Database credentials:');

console.log('User:', process.env.POSTGRES_USER);

console.log('Host:', process.env.POSTGRES_HOST);

console.log('Database:', process.env.POSTGRES_DATABASE);

console.log('Password:', process.env.POSTGRES_PASSWORD ? '****' : 'Not Set');

console.log('Port:', process.env.POSTGRES_PORT);

const pool = new Pool({

user: process.env.POSTGRES_USER,

host: process.env.POSTGRES_HOST,

database: process.env.POSTGRES_DATABASE,

password: process.env.POSTGRES_PASSWORD,

port: Number(process.env.POSTGRES_PORT),

});

// Function to fetch data for processing

export const databaseConnection = async () => {

let client;

try {

console.log('Attempting to connect to the database...');

client = await pool.connect();

console.log('Database connection successful.');

`const query = ``

SELECT

id AS "rowId",

contract_address AS "tokenAddress",

asset_id AS "assetId",

FROM public.custom_action_signal

ORDER BY RANDOM() -- Randomize the rows

LIMIT 50

\;`

const result = await client.query(query);

console.log('Query Results:');

console.table(result.rows);

return result.rows; // Return the rows to be used in createOffer.ts

} catch (error: unknown) {

if (error instanceof Error) {

console.error('Database connection or query failed:', error.message);

} else {

console.error('Unknown error occurred during database connection or query.');

}

process.exit(1);

} finally {

if (client) {

client.release();

console.log('Database connection closed.');

}

}

};

// Function to update a row in the custom_action_signal table

export const updateCustomActionSignal = async (rowId: number, orderHash: string) => {

try {

`const query = ``

UPDATE public.custom_action_signal

SET

done_custom_bot_action = true,

some_var = $1,

modified_timestamp = NOW()

WHERE id = $2

\;`

const values = [orderHash, rowId];

await pool.query(query, values);

console.log(\Successfully updated row with ID ${rowId}`);`

} catch (error) {

console.error(\Error updating row with ID ${rowId}:`, error);`

}

};

export default databaseConnection;

My set up is running the .ts build within WSL within VSCode. I got my IP address via "ipconfig", and then added that into the `pg_hba.conf`. As a check, I use the same credential and successfully log into the Postgresql db via the SQL shell (psql).

However, when I run my script, the same credential gives me this error: Database connection or query failed: connect ETIMEDOUT 192.168.1.875:5432

Again, I am not too familiar with this type of DB issues, and I can't figure out why I did the exact same step on another machine without any connection issue.

What can I try? How can I debug?

1 Comment
2025/01/30
05:49 UTC

1

How do enforce matching arguments in functions within an object?

Sorry for the wordy title. It's always hard for me to explain typescript stuff concisely!

My problem is pretty simple. Say you have an object type that has two functions. It looks like this:

type invalidator<TArgs extends unknown[]> = {
  fn: (...args: TArgs) => any;
  genSetKey: (...args: TArgs) => string;
};

The point of this type is that the arguments supplied to genSetKey must match the args supplied to fn. This works fine.

The problem is when I try to make an object type, where each property is one of these "invalidator" objects.

This is what I've tried so far:

type invalidatorObj<TInvalidatorMap extends Record<string, unknown[]>> = {
  [K in keyof TInvalidatorMap]: invalidator<TInvalidatorMap[K]>;
};

But TS doesn't care if I provide different argument types for each fn and genSetKey.

I'm guessing that the issue is that Record is too... loose? Like TS isn't keeping track of the relationship between each key and each value?

Here's a TS playground link demonstrating the lack of type errors

2 Comments
2025/01/30
04:17 UTC

1

Decorators using experimental definition without experimental tag

Having an issue getting decorators to use the TS5 definition. Using React 18/Vite. I have TS 5.7.3 installed in my project Checked npm list and that is the only version being used (no conflicting dependencies),

I do not have experimental decorators set (event tried explicitly setting it to false)

all my configs are referencing ESNext
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",

using plugins: [react({ tsDecorators: true })] in vite.config

but it's still using the 3 argument definition

(react-ts-swc template)

1 Comment
2025/01/29
22:41 UTC

3

Best way to handle backend authorization without RBAC / custom claims

Storing custom claims (like role='admin') on your auth tokens seems to be the most common way of handling authorization, but it seems less than ideal for some use cases since as far as I can tell permissions can't be instantly revoked (since the claims are stored on the auth token until it expires).

So it seems that to avoid this, the best way of handling authorization is to use DB queries. Only problem is, how do you make it as efficient as possible?

Right now I'm using Postgres / Drizzle / TS for my backend. My initial approach was to create reusable queries that checked authorization, like this:

const isAdmin = (clubId: number) => {
    const result = db.select... // Query for checking if user is admin of club with clubId
    return !!result
  }

And then you can call that from any given endpoint that requires admin status, and return error status if needed. But this adds an extra round-trip to the DB, which adds unnecessary latency, and sometimes more than one check is needed (e.g. isAdmin, isOwnerOfProperty, etc.)

How do you guys handle situations like these? I know I could just write the authorization logic into every individual query, but it seems like there'd be a better way to do that where you can reuse the logic.

Also, any thoughts on RLS? Seems like a pretty good solution but it doesn't appear to be super popular, and I'm wondering if there's a reason.

Thanks!

1 Comment
2025/01/29
17:44 UTC

11

LSP impacts due to typing

Recently I've played around with https://www.better-auth.com, and noticed 2 things:

  1. They are a bit lax with typing
  2. Using their project seems to kill vscodes intellisense

The first was sad, the second frustrating.

Looking into this, I found https://github.com/better-auth/better-auth/issues/1252 which has some suspicions but no real concrete answers or explanations. I sort of just shrugged at this, and decided to try and fix up their typing issue so it's a bit more pleasant to use.

Disclaimer I haven't looked into LSP or how is implemented and my suspicions are based on educated guesses of how I'd expect it to work

Now, after a few hours of reworking and cleaning up their types, I noticed a reasonable number of repeated inline types, and allot of ReturnType<typeof (some func)>, which got me wondering if the lack of explicit typing, and use of inline typing, would contribute to problems with LSP lookups due to difficulty indexing types?

As an additional, I'd appreciate if anyone could tell me if using the CPU profiling in tsc would provide adequate information to confirm my suspicions, or would it be more appropriate to dig into the LSP server to figure out the problems

5 Comments
2025/01/29
11:27 UTC

2

Type not getting inferred

SOLVED: Although, it is weird behavior, it looks as though if you have an implementation similar to mine where the Generic Parameter (TEvents per my example) is indirectly referenced in the class, then the inference of that generic parameter from child classes will fail.

e.g., per my example __listeners was declared as a type of Partial<Record<keyof TEvents, Function[]>>, so it was indirectly referencing the TEvents generic parameter, meaning inference from a child class would fail. However, if you have a variable that directly references the generic parameter (I added a #__ignore variable typed as TEvents) then type inference from a child class will successfully yield what you're looking for.

Non-working example:

type InferParam<T extends MyClass<any>> = T extends MyClass<infer U> ? U : never;

class MyClass<T extends Record<string, any>> {
}

class MyChildClass extends MyClass<{ test: number }> {}

const x: InferParam<MyChildClass> = {}; // typescript will NOT yell at you for this.

Working example:

type InferParam<T extends MyClass<any>> = T extends MyClass<infer U> ? U : never;

class MyClass<T extends Record<string, any>> {
  // notice this variable
  #__ignore: T;
}

class MyChildClass extends MyClass<{ test: number }> {}

const x: InferParam<MyChildClass> = {}; // typescript will yell at you
// expecting that `x` should be a type of `{ test: number }`

ORIGINAL QUESTION:

I am trying to make a custom AsyncEventEmitter class to handle asynchronous events. I was able to successfully get a class to work functionally, but I wanted to make a specific type for getting the Listener type, so I can declare functions separately. Here is a short excerpt of what I am working with on TS playground

Essentially, what I want is this:

class AsyncEventEmitter<TEvents extends Record<string, any[]>> {}

class MyAsyncEventEmitter extends AsyncEventEmitter<{ test: [a: number, b: string]}> {}

type AsyncEvents<TEmitter extends AsyncEventEmitter<any>> = TEmitter extends AsyncEventEmitter<infer TEvents> ? TEvents : never;

type AsyncEventListener<TEmitter extends AsyncEventEmitter<any>, TEvent extends keyof AsyncEvents<TEmitter>> = (...args: AsyncEvents<TEmitter>[TEvent]) => void|Promise<void>;

// forgive me, I write all of my TypeScript in JSDOC, 
// so I declare the function using JSDOC here since I don't remember off the top of my head on how to type a Function in TS

/** @type {AsyncEventListener<MyAsyncEventEmitter, "test">} */
function test(a,b) {
  // intellisense should pick up that parameter [a] is a number and parameter [b] is a string.
}

The above example is essentially what I have in the TS playground, but you'll see that the function test(a,b) does not properly infer the types a and b as number and string (respectively). Instead it is inferred as any and any.

If I explicitly use the AsyncEvents custom type, as defined above, then I will only ever get a Record<string, any[]> type.

e.g.,

const events: AsyncEvents<MyAsyncEventEmitter> = {
};

The above code, if AsyncEvents worked as I expected, should throw an error, as events should have the "test" key inside its object and a value for that property being [{number}, {string}].

Instead, events is typed as a Record<string, any[]>.

I've never had an issue with inferring generic type parameters before, so I am wondering what I could be doing differently that is causing this behavior.

Thank you in advance.

Another side-note: I am aware there is an async-event-emitter library, but this project requires us to have a little bit more control over the library, and it was simple enough to write one of our own.

8 Comments
2025/01/28
14:17 UTC

6

Started to learn TS, zero react knowledge

After embarrassing myself at a job interview I want to learn react, already started learning TS. Need recommendations on resources to learn both these technologies together.

Preferably through doing a project. Im just going to small YT tutorials and then documentation on internet to learn TS.

17 Comments
2025/01/27
17:38 UTC

5

Proper way to learn TS for react?

Hi am a full stack noob here learning react from odin project. I thought practicing ts from now itself will improve myself. But when developing react project i kinda hit my nerve with using TS and start to use 'any' type and sometimes I just code in js in a ts file.

9 Comments
2025/01/27
08:21 UTC

3

rewriting legacy code

Any tips on how to rewrite legacy php code (code igniter) into NESTJS and REACT , and migrating from MySQL5 to MySQL8 plus adding new features for a delivery like web app. note: I haven't worked in this project and I haven't even seen the code base yet, I start next week.

4 Comments
2025/01/26
20:10 UTC

21

Is there a reason why "satisfies" can't be used on type definitions?

The "satisfies" operator is one of my favorite newer TS features. I use it all the time. However, it seems arbitrarily limited to me to only be applied to expressions, but not to types themselves, and I was curious if anyone knows why this is?

For those wondering why I'd want it available at the type level, perhaps the most obvious use case is creating two types where you've guaranteed they at least share the same set of keys, even if their value types may differ. Of course, there are an infinite number of applications, but this is the most common. An example of what I wish we could write:

// I want to enforce, at the TYPE level, that the keys of RecordOne and RecordTwo are the same,
// so that a change in one spot to their keys forces both types to be updated.
type RecordOne = { key1: number, key2: number, key3: number };
type RecordTwo = { key1: string, key2: string, key3: string };

// So I declare a type with the keys:
type SharedKeys = 'key1' | 'key2' | 'key3';

// And then I use this type to enforce that the keys are the same, but I cannot because TS limits "satisfies" to only code expressions.
type RecordOneWithSatisfies = { key1: number, key2: number, key3: number } satisfies Record<SharedKeys, unknown>;
type RecordTwoWithSatisfies = { key1: string, key2: string, key3: string } satisfies Record<SharedKeys, unknown>;
44 Comments
2025/01/26
15:23 UTC

Back To Top