Type errors with nested object types
MMMikeM opened this issue · comments
Hi @luisherranz
I'd like to start off by saying I think the API you've written here is absolutely excellent. I've rewritten around 1000 lines of old contexts to deep signals, and everything is way easier to reason with.
I have run into one issue so far, and this might be entirely down to user error. I've narrowed things down to the minimal reproduction below
import { deepSignal } from "deepsignal";
type Example = {
testKey: Record<string, string | boolean>;
};
const initialState: Example = {
testKey: { testValue: false },
};
const state = deepSignal<Example>(initialState);
const testKey: Example["testKey"] = { testValue: true };
state.testKey = testKey;
I get the following error:
Type 'Record<string, string | boolean>' is not assignable to type 'DeepSignalObject<Record<string, string | boolean>>'.
Type 'Record<string, string | boolean>' is not assignable to type '{ [x: `$${string}`]: Signal<string | boolean> | undefined; }'.
'string' and '`$${string}`' index signatures are incompatible.
Type 'string | boolean' is not assignable to type 'Signal<string | boolean> | undefined'.
Type 'string' is not assignable to type 'Signal<string | boolean>'.ts(2322)
I see a similar error with
type Example = {
testKey: { [key: string]: string | boolean };
};
however if I change the string into a literal,
type Example = {
testKey: Record<"someLiteral", string | boolean>;
};
or
type Example = {
testKey: { ["someLiteral"]: string | boolean};
};
I no longer get the error. I have tried various permutations of using optional values / undefined unions, but to no real avail.
Do you see anything here that I'm doing incorrectly here?
Thanks very much!
The problem comes from that fact that deepSignal()
modifies the types of the objects to add the $prop
props.
const initialState = {
testKey: { testValue: true },
};
// this is not a deepsignal, $testValue doesn't exist
initialState.testKey.$testValue;
const state = deepSignal(initialState);
// this is a deepsignal, $testValue exists
state.testKey.$testValue;
You are trying to use a type that doesn't have those $prop
props in an object that is already a deep signal and therefore needs those $prop
props.
const testKey: Example["testKey"] = { testValue: true }; // this type is not a deep signal
state.testKey = testKey; // but now you are assigning it to a deep signal!
TypeScript can't convert types on assignment, so you have two options:
-
Transform the type into a deep signal before the assignment:
import { DeepSignal } from "deepsignal"; const testKey: DeepSignal<Example["testKey"]> = { testValue: true }; // this type is a deep signal state.testKey = testKey; // now TypeScript doesn't complain
This is technically not 100% correct because the
testKey
variable now hastestKey.$testValue
. -
Change the type on the assignment itself:
import { DeepSignal } from "deepsignal"; const testKey: Example["testKey"] = { testValue: true }; state.testKey = testKey as DeepSignal<typeof testKey>;
Hope this helps!
I'm closing this now as solved, but feel free to reopen it if you have more related questions.