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.

Readonly fields on inferred type

See original GitHub issue

Is there a way to get fields in the inferred type to be marked as readonly?

I’m thinking of something like this:

export const SCHEMA = v.object({
    regular_field: v.string(),
    optional_field: v.string().optional(),
    readonly_field: v.string().readonly(),
});
export type InferredType = v.Infer<typeof SCHEMA>;

…resulting in:

type InferredType = {
    optional_field?: string | undefined;
    regular_field: string;
    readonly readonly_field: string;
}

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
jviidecommented, Oct 31, 2021

After thinking about this for a while… I haven’t reached any good conclusion. But I have some thoughts, so prepare for a half-formed braindump 🙂

What is .readonly()?

I’m a bit worried about the fact that “readonly” tends to refer to two slightly different things in TypeScript. Having a .readonly() method could mean these two things:

  • Making all fields of v.object/v.record/v.tuple/v.array readonly (e.g. v.object({ a: ..., b... }).readonly()).
  • Making a specific field of v.object readonly (e.g. v.object({ a: v.number().readonly() })).

Therefore .readonly’s meaning would be somewhat overloaded, and the meaning of the following would be ambiguous:

v.object({
  a: v.array(v.number()).readonly()
});

Is the output type now { readonly a: readonly [number] }, { readonly a: [number] } or { a: readonly [number] }? 🙂

One solution for this would, of course, to have different named methods for those different use cases. Like v.array(...).immutable() for example. That’d be pretty descriptive. Maybe.

Helper functions

I’m not fully convinced this even needs to be in the Valita core. .assert, .map and .chain methods are pretty cool and open up interesting possibilities for implementing some functionality as external helper functions. For example, the “make some object fields readonly” can be implemented in the following way:

// Flatten an intersection { a: A } & { b: B } to { a: A, b: B }
type Flatten<V> = { [K in keyof V]: V[K] };

// Create a type predicate that asserts that the objects given fields are readonly.
function ro<O extends Record<string, unknown>, F extends (keyof O)[]>(
  ..._fields: F
) {
  return (
    o: O
  ): o is Flatten<Omit<O, F[number]> & Readonly<Pick<O, F[number]>>> => true;
}

const SCHEMA = v
  .object({
    regular_field: v.string(),
    optional_field: v.string().optional(),
    readonly_field: v.string(),
  })
  .assert(ro("readonly_field"));

type InferredType = v.Infer<typeof SCHEMA>;
// type InferredType = {
//   regular_field: string;
//   optional_field?: string | undefined;
//   readonly readonly_field: string;
// }

A similar solution can be cooked up for making the whole object / array readonly.

Let’s get radical

One interesting & bit radical approach would be to just make all Valita output readonly by default. It would have the additional benefit of discouraging people to mutate the output (and therefore accidentally mutating the input too, as Valita passes through the original object if it can get away with it). YMMV, of course 🙂

0reactions
marvinhagemeistercommented, Nov 1, 2021

No, it’s not the same. In TypeScript the optional modifier acts very different compared to readonly. It only applies to properties and you cannot mark, let’s say an array, as optional. An array can’t be optional, but an object property can. So there is no ambiguity regarding optional.

However, the readonly keyword can be used for two separate purposes:

interface Foo {
  readonly bar: string[]
}

// vs
interface Foo {
  bar: readonly string[]
}

That’s where the ambiguity comes from.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Typescript: restrict field type and infer rest fields - Stack Overflow
The question is how to tell TypeScript to infer generic param of certain fn call with literals as { field: Union, extra: number...
Read more >
Readonly type - Ballerina language
The readonly type consists of values that are immutable. For structural type T , T & readonly means immutable T . T &...
Read more >
Support `readonly` type operator to allow correct definition of ...
Typescript seems to drop readonly object fields at will when calling a ... Types are inferred too narrow from template strings in a...
Read more >
Understanding infer in TypeScript - LogRocket Blog
If the library doesn't provide a standalone type for the person argument of describePerson , defining a variable beforehand as the person ...
Read more >
Understanding and Embracing TypeScript's “readonly”
Object.freeze() also works on arrays, and TypeScript will infer the return type as ReadonlyArray ; (again, because its own type definitions ...
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