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

Satisfies operator doesnt carry over optional properties #52805

Closed
alesmenzel opened this issue Feb 16, 2023 · 5 comments
Closed

Satisfies operator doesnt carry over optional properties #52805

alesmenzel opened this issue Feb 16, 2023 · 5 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@alesmenzel
Copy link

Bug Report

🔎 Search Terms

satisfies, optional property

🕗 Version & Regression Information

  • This is a crash - No
  • This changed between versions - Since introduction of satisfies
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about satisfies operator
  • I was unable to test this on prior versions because satisfies operator was introduced in 4.9

⏯ Playground Link

Playground link with relevant code

💻 Code

type ListItem = {
    id: string
    label: string
    disabled?: boolean
}

const listA = [{
    id: 'foo',
    label: 'Foo',
    // disabled: true
}] as const satisfies readonly ListItem[]

// "Property 'disabled' does not exist on type"
// Should not error here, since the listA should satisfy ListItem type which has disabled property
// EXPECTED: listA[0].disabled has type boolean | undefined
listA[0].disabled
//          ^?

const listB = [{
    id: 'foo',
    label: 'Foo',
    disabled: true
}] as const satisfies readonly ListItem[]

// Here the type is correctly resolved to `true`
listB[0].disabled
//          ^?

🙁 Actual behavior

Readonly literal types that satisfy a type don't carry over optional properties that might not be set. Setting the type directly would mean we lose the literal types.

🙂 Expected behavior

Optional properties are carried over when a const satisfies some type. Expected type of listA is { id: 'foo', label: 'Foo', disabled?: boolean }[]

@alesmenzel
Copy link
Author

Workaround is to set the values as undefined explicitly.

const listA = [{
    id: 'foo',
    label: 'Foo',
    disabled: undefined
}] as const satisfies readonly ListItem[]

listA[0].disabled // undefined

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Feb 16, 2023
@RyanCavanaugh
Copy link
Member

This is the intended behavior of the satisfies operator. See discussion at #47920

@fatcerberus
Copy link

Huh, I recall that the primary debate over satisfies was basically between “type-check only” vs. “safe upcast” (with the former winning out); it never even struck me there was this third possibility of “infer ExpressionType & CheckType”; that’d have been kind of clever, actually.

To OP: Note that explicitly setting to undefined won’t work anymore if you ever decide to turn on exactOptionalPropertyTypes.

@alesmenzel
Copy link
Author

It would be nice to have some way to define that a constant variable conforms to some type and would carry over the type. @RyanCavanaugh Maybe could be a new feature request? or what are your thoughts on this topic?

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants