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 fails to recognize optional properties that are defined #54836

Closed
UselessMnemonic opened this issue Jun 29, 2023 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@UselessMnemonic
Copy link

UselessMnemonic commented Jun 29, 2023

Bug Report

πŸ”Ž Search Terms

"partial" "satisfies" "narrow"

πŸ•— Version & Regression Information

Tried on version 5.1.5

⏯ Playground Link

https://www.typescriptlang.org/play?exactOptionalPropertyTypes=true#code/C4TwDgpgBAqgzhATlAvFA3gWAFBT1RCAQwBMB7AOwBsQoBLEgLijmETooHMAaHfA4uWq04ZALYQAggGNgdSswAUASlQA+KADcyDHAF8cOUJCgBZAK7AiAIyoQAPABUNaLLnwBaQqUo0oAbQBpegooAGsIEDIAMyhHAF1mRyD4-UNsaPMKWXlQ6W9gCHgkRQZmVnYuZWZi5Dd+aUpWKAoIAHda5gsrWwcABSJEOSIqe1q1Fwx6Eig9AG50-laOhEQAOlEJGTlKVCgVdSmAegAqKDWLqBOj2YXsPnxCYHNEUOXaliI5OGi6CDhYKs5ngjjdokQ6FQ4GlsEA

πŸ’» Code

type User = {
    readonly id: string,
    readonly someAction: () => void
}

type UnfinishedUser = {
    id: string,
    someAction?: () => void
}

function createUser(id: string): User {
    const newUser: UnfinishedUser = { id };

    newUser.someAction = () => { /* ... */ };

    return newUser satisfies User;  // fails
}

πŸ™ Actual behavior

newUser does not satisfy User

πŸ™‚ Expected behavior

newUser should satisfy User

@jcalz
Copy link
Contributor

jcalz commented Jun 30, 2023

As far as I know, existingVariable satisfies Type has essentially no effect, so it's not surprising that satisfies isn't doing anything here. I'd say satisfies is a red herring and the underlying issue is that assigning the someAction property of newUser doesn't narrow newUser itself, and as such this is a duplicate of #42384. If that were to be implemented then your code would start working (but you still wouldn't need satisfies). For example:

// simulate #42384
function addProp<T extends object, K extends PropertyKey, V>(
    obj: T, k: K, v: V): asserts obj is T & Record<K, V> {
    Object.assign(obj, { [k]: v });
}

function createUser(id: string): User {
    const newUser: UnfinishedUser = { id };
    addProp(newUser, "someAction", () => { /* ... */ });
    return newUser satisfies User;  // okay
}

function createUser2(id: string): User {
    const newUser: UnfinishedUser = { id };
    addProp(newUser, "someAction", () => { /* ... */ });
    return newUser;  // still okay
}

Playground link

@UselessMnemonic
Copy link
Author

UselessMnemonic commented Jun 30, 2023

I understand, I think what I'm looking for is more explicit type guards. Using as in the return statement, although subtle, better conveys the behavior I desire:

type User = {
    readonly id: string,
    readonly someAction: () => void
}

type UnfinishedUser = {
    id: string,
    someAction?: () => void
}

function createUser(id: string): User {
    const newUser: UnfinishedUser = { id };

    newUser.someAction = () => { /* ... */ };

    return newUser satisfies User as User;  // fails
}

My goal is to ensure newUser does in fact satisfy User without satisfies having a side-effect on the type itself (as if to cast, like my initial suggestion implies.) Clearly as will succeed anyways, but not without loosing some type safety. The two operators could work well in tandem, if only satisfies was smarter.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jul 6, 2023
@typescript-bot
Copy link
Collaborator

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

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Jul 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants