Skip to content

.min or .destruct on .constructed fall back to main type #79

Description

@phryneas

Hey there again, sorry that I keep digging up weird stuff :)
I have created a constructed type with

const stringOrObjectWithKey = string.construct(
  (arg: { key: string } | string) =>
    typeof arg === "string" ? [arg] : [arg.key]
);

and I can call that just fine like

stringOrObjectWithKey("asd");
stringOrObjectWithKey({ key: "asd" });

now, when I chain that like

const withMin = stringOrObjectWithKey.min(1);

I lose the ability to call it with the overload:

withMin("asd");
// Argument of type '{ key: string; }' is not assignable to parameter of type 'string'.ts(2345)
withMin({ key: "asd" });

Looking at the types, I don't really see what is wrong there - and to be honest, these types are quite complicated to get started with so I'm a bit hesitant to do a PR on this.

I have, though, created a type test for this that you can use to verify this behaviour:
CodeSandBox

I hope that helps to pinpoint it down.

Please don't hesitate to contact me if I can help any further 🙂

/*eslint-disable @typescript-eslint/no-unused-vars */

import Schema, { string } from "computed-types";
import { SchemaResolveType, SchemaInput } from "computed-types/lib/schema/io";

function expectType<T>(t: T) {
  return t;
}
function expectAssignable<Subtype, _Type extends Subtype>() {}

const stringOrObjectWithKey = string.construct(
  (arg: { key: string } | string) =>
    typeof arg === "string" ? [arg] : [arg.key]
);

stringOrObjectWithKey("asd");
stringOrObjectWithKey({ key: "asd" });
expectType<(v: string | { key: string }) => string>(stringOrObjectWithKey);
expectAssignable<
  string | { key: string },
  SchemaInput<typeof stringOrObjectWithKey>
>();
expectAssignable<
  SchemaInput<typeof stringOrObjectWithKey>,
  string | { key: string }
>();

const destructed = stringOrObjectWithKey.destruct();
destructed("asd");
destructed({ key: "asd" });
expectType<(v: string | { key: string }) => [any, string?]>(destructed);
// second signature missing here
expectType<(v: string) => [any, string?]>(destructed);
expectAssignable<string | { key: string }, SchemaInput<typeof destructed>>();
expectAssignable<SchemaInput<typeof destructed>, string | { key: string }>();
expectAssignable<[any, string?], SchemaResolveType<typeof destructed>>();
expectAssignable<SchemaResolveType<typeof destructed>, [any, string?]>();

const withMin = stringOrObjectWithKey.min(1);
withMin("asd");
withMin({ key: "asd" });
expectType<(v: string | { key: string }) => string>(withMin);
// second signature missing here
expectType<(v: string) => string>(withMin);
expectAssignable<string | { key: string }, SchemaInput<typeof withMin>>();
expectAssignable<SchemaInput<typeof withMin>, string | { key: string }>();
expectAssignable<string, SchemaResolveType<typeof withMin>>();
expectAssignable<SchemaResolveType<typeof withMin>, string>();

const inSchema = Schema({ test: stringOrObjectWithKey });
inSchema({ test: "asd" });
inSchema({ test: { key: "asd" } });
expectType<(v: { test: string | { key: string } }) => { test: string }>(
  inSchema
);
expectAssignable<
  { test: string | { key: string } },
  SchemaInput<typeof inSchema>
>();
expectAssignable<
  SchemaInput<typeof inSchema>,
  { test: string | { key: string } }
>();

const destructedSchema = inSchema.destruct();
destructedSchema({ test: "asd" });
destructedSchema({ test: { key: "asd" } });
expectType<(v: { test: string | { key: string } }) => [any, { test: string }?]>(
  destructedSchema
);

const mergedSchema = Schema.merge({ other: string.optional() }, inSchema);
mergedSchema({ test: "asd" });
mergedSchema({ test: { key: "asd" } });
expectType<(v: { test: string | { key: string } }) => { test: string }>(
  mergedSchema
);
expectAssignable<
  { test: string | { key: string } },
  SchemaInput<typeof mergedSchema>
>();
expectAssignable<
  SchemaInput<typeof mergedSchema>,
  { test: string | { key: string } }
>();

const mergedDestructedSchema = mergedSchema.destruct();
mergedDestructedSchema({ test: "asd" });
mergedDestructedSchema({ test: { key: "asd" } });
expectType<(v: { test: string | { key: string } }) => [any, { test: string }?]>(
  mergedDestructedSchema
);

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions