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

Optional property set to undefined fails validation #25

Closed
blombern opened this issue May 20, 2019 · 2 comments
Closed

Optional property set to undefined fails validation #25

blombern opened this issue May 20, 2019 · 2 comments

Comments

@blombern
Copy link

interface Test {
  a?: string
}
assertType<Test>({ a: undefined })
// TypeGuardError: validation failed at $.a: expected a string

Is this expected behavior? I thought a?: string and a: string | undefined were equivalent in TypeScript.

@woutervh-
Copy link
Owner

Hi @blombern

Yes this is expected behavior. I think it's a bit quirky the way TypeScript deals with optional properties and undefined. The reason I chose to make a distinction between the two is because {} and {a: undefined} are different at run time with respect to operations like Object.keys etc.

Also, a?: string and a: string | undefined are not exactly the same in TypeScript either:

interface Foo {
  foo: string | undefined;
}

const x: Foo = {}; // <--- error: missing property "foo"

I think keeping the distinction allows for more precise definitions of the types, either some property is always there and possibly undefined, or some property can be missing.

@dko-slapdash
Copy link
Contributor

dko-slapdash commented Mar 23, 2021

@woutervh- I want to re-raise this question still since the behavior of TS and behavior of typescript-is is different here.

An example:

assertType<{ a?: number }>({ a: undefined });
// raises TypeGuardError: validation failed at $.a: expected a number, found: undefined

At the same time, the following code is perfectly fine (and is by design) in TS:

let z: { a?: number; b: string };
z = { a: undefined, b: "bb" };
// ^ works fine

Above, you mention that a?: string and a: string | undefined (without question mark)/ are different in TS. Yes, indeed they are. But the question in the current issue is not about this, it's about a?: string being able to accept undefined too, which works in TS; i.e. it's about equality of a?: string and a?: string | undefined (with question mark in both cases).

I.e. in TS, a?: string and a?: string | undefined are exactly the same type. There is a large GitHub issue in TS repo: microsoft/TypeScript#13195 about this. The idea is that, even if you have an { a?: string } type in TS, once you start doing something with this type (apply Required or Partial or keyof or whatever), it automatically becomes { a?: string | undefined }, and there is no way back (nor there is a way to distinguish between these two types).

I think typescript-is'es goal is to be as close to TS native behavior as possible (that's the power of this library), and also it's mostly used for JSON-deserialized values which by definition cannot have undefined in them:

JSON.stringify({ a: undefined }); // "{}"
JSON.stringify([1, undefined, 2]); // "[1,null,2]"

So in practice, even if typescript-is allows people assign undefined to ?-properties, this will not break compatibility much.

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

No branches or pull requests

3 participants