unknownutil icon indicating copy to clipboard operation
unknownutil copied to clipboard

Refine `isReadonlyOf` and related

Open lambdalisue opened this issue 1 year ago • 3 comments

TypeScript offers the Readonly<T> type function and the readonly keyword. However, the isReadonlyOf function seems a bit ambiguous. I propose the following improvements:

  • Enhance isReadonlyOf for Array, Tuple, and Object types.
  • Introduce isReadonlyFieldOf for checking/annotating the readonly status of object fields.
  • Rename isOptionalOf to isOptionalFieldOf to maintain consistency with isReadonlyFieldOf. This change aligns with the handling of ? for object fields.

lambdalisue avatar Feb 13 '24 12:02 lambdalisue

Several pattern to annotate fields as ? or readonly.

import { is, Predicate } from "./mod.ts";

function optional<K extends PropertyKey>(name: K): K {
  return name;
}

function readonly<K extends PropertyKey>(name: K): K {
  return name;
}

function annotate<T>(
  pred: Predicate<T>,
  annotations: { optional?: boolean; readonly?: boolean },
): Predicate<T> {
  return pred;
}

const as = {
  Optional: <T>(pred: Predicate<T>) => pred,
  Readonly: <T>(pred: Predicate<T>) => pred,
};

const pattern1 = is.ObjectOf({
  [optional("optionalField")]: is.String,
  [readonly("readonlyField")]: is.String,
  [readonly(optional("readonlyOptionalField"))]: is.String,
});

const pattern2 = is.ObjectOf({
  optionalField: annotate(is.String, { optional: true }),
  readonlyField: annotate(is.String, { readonly: true }),
  readonlyOptionalField: annotate(is.String, {
    optional: true,
    readonly: true,
  }),
});

const pattern3 = is.ObjectOf({
  optionalField: as.Optional(is.String),
  readonlyField: as.Readonly(is.String),
  readonlyOptionalField: as.Readonly(as.Optional(is.String)),
});

I personally like as.* pattern

lambdalisue avatar Mar 06 '24 04:03 lambdalisue

The optional annotation is also used as an element of isTupleOf and isParametersOf, so it is impossible to apply pattern 1 of annotating object keys.

Milly avatar Apr 15 '24 01:04 Milly

What I'm thinking of it is

  • as.Optional - Optional for a field or an element
  • as.Readonly - Readonly for a field or an element
  • is.PartialOf - Optional for all fields or elements of object, array, tuple, etc.
  • is.ReadonlyOf - Readonly for all fields or elements of object, array, tuple, etc.
type A = {
  foo?: string;
  readonly bar: string;
  readonly hoge?: string;
};

// Predicate<A>
const a = is.ObjectOf({
  foo: as.Optional(is.String),
  bar: as.Readonly(is.String),
  hoge: as.Optional(as.Readonly(is.String)),
});

type B = Partial<A>;

// Predicate<B>
const b = is.PartialOf(a);

type C = Readonly<A>;

// Predicate<C>
const c = is.ReadonlyOf(a);

lambdalisue avatar Apr 15 '24 01:04 lambdalisue

#87

lambdalisue avatar Aug 01 '24 21:08 lambdalisue