Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exact does not work as expected with type involving Date and union #896

Closed
hgossler opened this issue Jun 20, 2024 · 2 comments · Fixed by #902
Closed

Exact does not work as expected with type involving Date and union #896

hgossler opened this issue Jun 20, 2024 · 2 comments · Fixed by #902

Comments

@hgossler
Copy link

hgossler commented Jun 20, 2024

In the example below, the cases t7 and t8 cause a TS error.

import type { Exact } from "type-fest";

interface A {
	a: string;
	b?: Date | null;
}

function testArg<T extends Exact<A, T>>(a: T) {
	return a;
}

const t1 = { a: "a" };
testArg(t1); // OK - Type of arg correct, no TS error

const t2 = { a: "a", b: new Date() };
testArg(t2); // OK - Type of arg correct, no TS error

const t3 = { a: "a", b: null };
testArg(t3); // OK - Type of arg correct, no TS error

const t4 = { a: "a", b: undefined };
testArg(t4); // OK - Type of arg correct, no TS error

const t5 = { a: "a", b: new Date() as Date | null | undefined };
testArg(t5); // OK - Type of arg correct, no TS error

const t6 = { a: "a", b: null as null | undefined };
testArg(t6); // OK - Type of arg correct, no TS error

const t7 = { a: "a", b: new Date() as Date | undefined };
testArg(t7); // Bug - Type of arg correct, but TS error

const t8 = { a: "a", b: new Date() as Date | null };
testArg(t8); // Bug - Type of arg correct, but TS error

Error at testArg(t7):

error TS2345: Argument of type '{ a: string; b: Date | undefined; }' is not assignable to parameter of type 'ExactObject<A, { a: string; b: Date | undefined; }>'.
  Type '{ a: string; b: Date | undefined; }' is not assignable to type '{ a: string; b?: ExactObject<Date, Date | undefined> | null | undefined; }'.
    Types of property 'b' are incompatible.
      Type 'Date | undefined' is not assignable to type 'ExactObject<Date, Date | undefined> | null | undefined'.
        Type 'Date' is not assignable to type 'ExactObject<Date, Date | undefined>'.
          Type 'Date' is not assignable to type '{ toString: ExactObject<() => string, never>; toDateString: ExactObject<() => string, never>; toTimeString: ExactObject<() => string, never>; ... 41 more ...; [Symbol.toPrimitive]: ExactObject<...>; }'.
            Types of property 'toString' are incompatible.
              Type '() => string' is not assignable to type 'ExactObject<() => string, never>'.
                Type '() => string' is not assignable to type 'Record<string | number | symbol, never>'.
                  Index signature for type 'string' is missing in type '() => string'.

Error at testArg(t8):

error TS2345: Argument of type '{ a: string; b: Date | null; }' is not assignable to parameter of type 'ExactObject<A, { a: string; b: Date | null; }>'.
  Type '{ a: string; b: Date | null; }' is not assignable to type '{ a: string; b?: ExactObject<Date, Date | null> | null | undefined; }'.
    Types of property 'b' are incompatible.
      Type 'Date | null' is not assignable to type 'ExactObject<Date, Date | null> | null | undefined'.
        Type 'Date' is not assignable to type 'ExactObject<Date, Date | null>'.
          Type 'Date' is not assignable to type '{ toString: ExactObject<() => string, never>; toDateString: ExactObject<() => string, never>; toTimeString: ExactObject<() => string, never>; ... 41 more ...; [Symbol.toPrimitive]: ExactObject<...>; }'.
            Types of property 'toString' are incompatible.
              Type '() => string' is not assignable to type 'ExactObject<() => string, never>'.

Upvote & Fund

  • We're using Polar.sh so you can upvote and help fund this issue.
  • The funding will be given to active contributors.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar
@sindresorhus
Copy link
Owner

// @zorji

@zorji
Copy link
Contributor

zorji commented Jun 30, 2024

I originally created this Exact type for myself to check API input to avoid excess fields (i.e. the OpenAI spec additionalProperties).

It looks like the implementation is not quite compatible with class with methods etc.

I add a fix here #902

The solution simply only apply Exact on JsonObject, assuming it ignores all types from a TypeScript class.

@hgossler

In additional to your test cases, I think the following should fail

const t4 = { a: "a", b: undefined };
const t5 = { a: "a", b: new Date() as Date | null | undefined };
const t6 = { a: "a", b: null as null | undefined };
const t7 = { a: "a", b: new Date() as Date | undefined };

Allowing optional field does not mean it should accept undefined. It's passing in your side probably because your setting did not enable strict null check https://www.typescriptlang.org/tsconfig/strictNullChecks.html

In the fix, the above case will still be rejected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants