#Type Definition Best Practices with React

1 messages · Page 1 of 1 (latest)

gaunt thicket
#

Hi all~!

When defining types, I can mainly think of 3 common ways for doing it:

1 - Defining the "actual" type
e.g.

interface MyType {
  foo: number;
}

2 - Passing the related type alias

type MyNumericType = number;

interface MyType {
  foo: MyNumericType;
}

3 - Indexed access type

type MyNumericType = number;

interface MyIndexedType {
  bar: MyNumericType;
}

interface MyType {
  foo: MyIndexedType['bar']; // instead of `MyNumericType` or `number`
}

So I was wondering if there were commonly known best practices to know which way is more suitable?

In my case, I am often facing below situation and never sure which way to pick.

type MyCustomType = number | string;

type ComponentCProps = {
  c: MyCustomType;
}

function ComponentC({ c }: ComponentCProps) {
  // ...
}

type ComponentBProps = {
  b: MyCustomType;
}

function ComponentB({ b }: ComponentBProps) {
  // ...
}

type ComponentAProps = {
  a: boolean;
  b: number | string; // OR ComponentBProps['b'] ? OR MyCustomType ?
  c: number | string; // OR ComponentCProps['c'] ? OR MyCustomType ?
}

function ComponentA({ a, b, c }: ComponentAProps) {
  if (!a) return null;

  return (
    <div>
      <ComponentB b={b} />
      <ComponentC c={c} />
    </div>
  );
}
  1. seems clearer than 2) and 3) but harder to maintain, 2) sounds easier to maintain but seems not really convenient to always have to dig into the type definitions and see what type/type definition I need to put in my parent component, 3) seems the easiest to use but not so clean...

Any idea perhaps?

magic stone
#

React is an UI framework
even tho it uses the same data as your model to display it, there should still be some kind of separation

#

my point is, I don't think using indexed access, even tho it's really convenient, is a good idea

#

yes, you can easily update your model, but the your view is automatically updated as well, and you might not be aware (if they don't create an error) or want those changes

#

that being said, I think it's best to simply write an interface for the props for every component
with the right expected types

#

if you have a ComponentA taking a b
that b should be of type number | string
not something linked to the model,
nor a custom type alias created just for that single property (MyCustomType)

#

This is how I woud write it

export function ComponentA({ a, b, c }: ComponentAProps) {
  if (!a) return null;

  return (
    <div>
      <ComponentB b={b} />
      <ComponentC c={c} />
    </div>
  );
}

type ComponentAProps = {
  a: boolean;
  b: number | string;
  c: number | string;
}
#

it might need a bit more effort to maintain, especially when you're makin changes
but on the other hand, you can detect "invisible" breaking changes (the types chainging and not display the way they used to) more easily

magic stone
#

@gaunt thicket