Skip to content

ThisType doesn't work with objects in array #33392

Closed
@Akryum

Description

@Akryum

TypeScript Version: 3.7.0-dev.20190912

Search Terms: thistype array

Code

interface Instance {
  foo: 'bar' | 'baar'
}

type Fn = () => any | any

interface Sub {
  vars: Fn
}

interface Options {
  subs: Sub | Sub[]
}

const meow: Options & ThisType<Instance> = {
  subs: {
    vars () {
      return this.foo === 'bar'
    }
  }
}

const meows: Options & ThisType<Instance> = {
  subs: [
    {
      vars () {
        // Error happens here
        return this.foo === 'bar'
      }
    }
  ]
}

Expected behavior:

No errors.

Actual behavior:

error TS2339: Property 'foo' does not exist on type 'Sub'.

Playground Link:

https://www.typescriptlang.org/play/#code/JYOwLgpgTgZghgYwgAgJIgM5jiJyDeAUMsjAPZkBcyA5AEZxQ3IA+tDjNhAvoYWAE8ADigBiIZAF5kACgCUUgHzIcA1ipAC+oSLEQoAygFc6BYsgBujDNXE9t4aPDwB5IWGBlMZkhhM3kY1M2IIBtAF17QgQvLGQAWwgyAHdqNw9Y5AAyZAAVAAtgDFzhCAAedCwcJGVpIl9-anqSS2tZBWaW5CgIMCMoCTBCjAA6cjIpSWl6TnMSXnmomMwwBKTkgPTPbxyCopKRCpXqiFqfZD86ANC5866rKAx2u67u3v7B4bGKSemGJluiy6C2QkV4QA

Related Issues:

Activity

j-oliveras

j-oliveras commented on Sep 12, 2019

@j-oliveras
Contributor

The this inside meows is a reference to the object inside the array, not the object meows. You can test it with node:

var meows = {
  subs: [
    {
      vars () {
        this.foo = 'hello' // assigned to display what is this here
        // Error happens here
        return this.foo === 'bar'
      }
    }
  ]
}
console.log(meows) // { subs: [ { vars: [Function: vars] } ] }
meows.subs[0].vars()
console.log(meows) // { subs: [ { vars: [Function: vars], foo: 'hello' } ] }
Akryum

Akryum commented on Sep 12, 2019

@Akryum
Author

It will do the same with:

const meow = {
  subs: {
    vars () {
      this.foo = 'hello'
      return this.foo === 'bar'
    }
  }
}
console.log(meows) // { subs: { vars: [Function: vars] } }
meows.subs.vars()
console.log(meows) // { subs: { vars: [Function: vars], foo: 'hello' } }

That's not the point of this issue, those methods a bound internally by my library.

fatcerberus

fatcerberus commented on Sep 13, 2019

@fatcerberus

Pretty sure ThisType only affects the object directly assigned to it, not sub-objects, whose properties have their own types (and therefore don't inherit the ThisType).

Akryum

Akryum commented on Sep 13, 2019

@Akryum
Author

I guess it's necessary, otherwise Vue typings wouldn't work 😁

RyanCavanaugh

RyanCavanaugh commented on Sep 13, 2019

@RyanCavanaugh
Member

@fatcerberus is correct

weswigham

weswigham commented on Sep 13, 2019

@weswigham
Member

FYI, if you want a deeply bound ThisType, you can do something like

type DeepApplyThisType<T, TThis> = {[K in keyof T]: DeepApplyThisType<T[K], TThis>} & ThisType<TThis>;

but it's on you to make sure your library can actually bind the this's of those functions up to match that.

Akryum

Akryum commented on Sep 14, 2019

@Akryum
Author

@RyanCavanaugh I've put a link in this message showing the inconsistency in nested objects.

typescript-bot

typescript-bot commented on Sep 16, 2019

@typescript-bot
Collaborator

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

Akryum

Akryum commented on Sep 19, 2019

@Akryum
Author

Using a custom type DeepApplyThisType breaks other types:

Example

Akryum

Akryum commented on Sep 19, 2019

@Akryum
Author

Could the issue be re-open? It causes me a lot of difficulties for vue-apollo typings.

dragomirtitian

dragomirtitian commented on Sep 20, 2019

@dragomirtitian
Contributor

@Akryum DeepApplyThisType works but does not cover all cases as it should. It should distribute over T[K] to correctly apply this in a union, and should not perform the transformation on function or primitives. This works with your example:

type DistributionHelper<T, TThis> = T extends Function | string | number | boolean ? T: DeepApplyThisType<T, TThis>

type DeepApplyThisType<T, TThis> = {
  [K in keyof T]: DistributionHelper<T[K], TThis>
} & ThisType<TThis>;

play

Other corner cases may arise but they can probably be solved.

Akryum

Akryum commented on Sep 20, 2019

@Akryum
Author

Thanks, will try it

Akryum

Akryum commented on Sep 22, 2019

@Akryum
Author

It's still doesn't work, the types are still broken:

Play

No error is thrown here:

const apollo2: DeepApplyThisType<ApolloOptions, Instance> = {
  $client: '42',
  foo: {
    // No error thrown here
    variableeees () {
      return {
        bar: this.foo
      }
    },
    update: (data) => data.foo
  }
}
Akryum

Akryum commented on Sep 30, 2019

@Akryum
Author

Please can this be reopen? 😅

Akryum

Akryum commented on Feb 11, 2021

@Akryum
Author

💀

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

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @Akryum@weswigham@fatcerberus@RyanCavanaugh@dragomirtitian

        Issue actions

          ThisType doesn't work with objects in array · Issue #33392 · microsoft/TypeScript