proposal-signals / signal-polyfill

Implementation tracking the current state of https://github.com/tc39/proposal-signals
Apache License 2.0
185 stars 13 forks source link

Computed is not assignable to computed with broader type #7

Open temoncher opened 4 months ago

temoncher commented 4 months ago

I work on a small form library based on signals and discovered an annoying issue with computeds

import { Signal } from 'signal-polyfill';

interface Broader {
  strProp: string;
}

interface Narrower extends Broader {
  numProp: number;
}

const state = new Signal.State<Narrower>({
  strProp: '',
  numProp: 33,
});

// not assignable, but should be, right?
const derived: Signal.Computed<Broader> = new Signal.Computed(() => state.get());

It impairs ability to consume computed signals, for example:

interface FormState<T> {
  value: T;
  errors: unknown;
}

class FormControl<T> {
  #state: Signal.State<FormState<T>>;
  readonly $state = new Signal.Computed(() => this.#state.get());
  constructor(intiValue: T) {
    this.#state = new Signal.State<FormState<T>>({
      value: intiValue,
      errors: null,
    });
  }
}

const numControl = new FormControl(0);

function valueConsumer(onlyValueState: Signal.Computed<{ value: number }>) {
  console.log(onlyValueState.get().value);
}

// errors, Argument of type 'Computed<FormState<number>>' is not assignable to parameter of type 'Computed<{ value: number; }>'
valueConsumer(numControl.$state);

repro: https://stackblitz.com/edit/vitejs-vite-jueqfh?file=src%2Fmain.ts