Skip to content

Partials accept functions #17003

Description

@luke-john

TypeScript Version: 2.4.0 / nightly (2.5.0-dev.201xxxxx)

2.4.1 and 2.5.0-dev.20170629 and 2.5.0-dev.20170707

Code

interface Foo {
    cat: string
    bar: number
}

type Bar = Partial<Foo>

const notBar: Bar = () => { } // passes on 2.4.1 and 2.5.0-dev.20170629 and 2.5.0-dev.20170707

Expected behavior:

Partials should not accept functions

Actual behavior:

Partials accept functions

typescript-playground-example

Related Issues:

13132 has been marked as a duplicate of 7485 which has been closed

7485 is meant was meant to be closed by 16047 which has merged.

Edit: updated after testing on latest nightly and simplified example code

Activity

  1. changed the title [-]Bug: Partials accept functions[/-] [+]Partials accept functions[/+] on Jul 7, 2017
  2. kitsonk commented on Jul 7, 2017

    @kitsonk
    Contributor

    Addressed in TypeScript FAQs.

  3. luke-john commented on Jul 7, 2017

    @luke-john
    Author

    Apologies but it's not clear to me why a function is assignable to a partial interface based on that.

    My issue is not to do with the parameters on the function, but that a function is assignable to a partial interface

  4. kitsonk commented on Jul 7, 2017

    @kitsonk
    Contributor

    Ah, I see the challenge...

    From a type perspective, functions are a type of object, which can have properties. You are assigning a function to an object with no required properties. Therefore, a function with no properties is assignable to an interface with all optional properties.

    Why all the cases you have in your playground example was because TypeScript did not detect weak types properly, and as of 2.4 it does. That means that strings and numbers are no longer assignable but these weak types.

    I tried to use object & Partial<T> as a type to avoid functions being assignable, but I am afraid functions are still considered subtypes of objects by the type system. But I might be missing something...

  5. luke-john commented on Jul 8, 2017

    @luke-john
    Author

    Edit: Upon trying the below unfortunately it does not work due to an issue with unions

    interface SinglePropA { a: 'test-a' }
    interface SinglePropB { b: 'test-b' }
    
    type SubsetAnimal =
        | SinglePropA
        | SinglePropB
    
    const testAandB: SubsetAnimal = {
        a: 'test-a',
        b: 'test-bs'
    } // passes on 2.4.1 and 2.5.0-dev.20170629 

    So after sleeping on it, I think I can restructure my use of Partial to achieve what I want with the following.

    type ValueOne = { valueOne: 'value-one' }
    type ValueTwo = { valueTwo: 'value-two' }
    
    type SingleValue =
        | ValueOne
        | ValueTwo
    
    type MultiValue =
        & Partial<
            & ValueOne
            & ValueTwo
        >
        & SingleValue
    
    type Value = SingleValue | MultiValue
    
    type Method = () => Value
    
    type Argument = Method | Value
    
    type Library = (argument: Argument) => void
    
    const library: Library = (props) => {}
    
    library({
    })
    
    library(() => ({
    }))

    typescript-playground-example

    Cheers for looking into it

  6. luke-john commented on Jul 10, 2017

    @luke-john
    Author

    Adding an index signature to the interface you pass to the Partial prevents it accepting functions.

    typescript playground example

    interface Foo {
        cat: string
        bar: number
    
        [key: string]: string | number | undefined
    }
    
    type Bar<Baz> =
        | Partial<Baz>
        | (
            () => Partial<Baz>
        )
    
    const notBar: Bar<Foo> = () => { } // fails on 2.4.1 and 2.5.0-dev.20170629 
    
    const alsoNotBar: Bar<Foo> = 'test' // fails on 2.4.1
    
    const alsoAlsoNotBar: Bar<Foo> = false // fails on 2.4.1

    edit: note, this does kill autocomplete though :/

  7. luke-john commented on Jul 14, 2017

    @luke-john
    Author

    just updating for anyone who comes across this via google.

    on 2.4.1 the following works and retains autocomplete.

    interface StylesComplete {
      propA: 'letter a' | 'a',
      propB: 'letter b' | 'b',
    }
    
    // A Partial on an interface will normally accept arbitrary functions
    // due to partials accepting empty objects which validate to anything in
    // typescript
    const doesNotFail: Partial<StylesComplete> = () => { }
    
    
    interface StylesLossy {
      [key: string]: string | undefined
    }
    
    type Styles =
      & StylesComplete
      & StylesLossy
    
    // intersection of
    // * an interface containing random props
    // * index signature interface
    // allows  you to do a partial which will successfully fail on arbitrary
    // functions.
    
    // successfully fails
    const doesFail: Partial<Styles> = () => { }
    

    typescript-playground-example

  8. locked and limited conversation to collaborators on Jun 14, 2018
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

    QuestionAn issue which isn't directly actionable in code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions