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

ThisType should use the intrinsic keyword #46836

Open
5 tasks done
DetachHead opened this issue Nov 17, 2021 · 6 comments
Open
5 tasks done

ThisType should use the intrinsic keyword #46836

DetachHead opened this issue Nov 17, 2021 · 6 comments
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@DetachHead
Copy link
Contributor

Suggestion

πŸ” Search Terms

thistype intrinsic

βœ… Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

ThisType is quite confusing in that it appears to do nothing when you go to its definition:

interface ThisType<T> { }

but it actually has secret intrinsic functionality? so imo it should use the intrinsic keyword:

type ThisType<T> = intrinsic

or better yet, why not implement it in the language?

type ThisType<OldThis, NewThis> = {
    [K in keyof OldThis]: OldThis[K] extends (...args: never[]) => unknown
        ? (this: NewThis, ...args: Parameters<OldThis[K]>) => ReturnType<OldThis[K]>
        : OldThis[K]
}

(though that would probably break existing code)

motivation

it's misleading in a language when a symbol has a "fake" implementation you can look at, but its behavior is actually defined intrinsically somewhere else. for example, the NoReturn type in python:

NoReturn = Union[None]

this is as non-sensical as its typescript equivalent:

type never = undefined

Union[None] has nothing to do with NoReturn. its behavior is actually intrinsically defined in type checkers such as mypy. if never had a fake definition like this, it would be extremely confusing and it would blur the lines between what's intrinsic and what isn't.

@fatcerberus
Copy link

Ironically Union[None] is pretty much exactly what never is in TypeScript, i.e. an empty union. type never = undefined seems like a poor comparison, since undefined is a unit type, not a bottom type.

@DetachHead
Copy link
Contributor Author

Union[None] in python doesn't actually mean an empty union. it's a union with one type in it (None), so if you attempt to recreate it, it behaves differently:

from typing import Union, NoReturn

NoReturn2 = Union[None]

foo: NoReturn
foo2: NoReturn2

bar: int = foo # no error
bar2: int = foo2 # error: Incompatible types in assignment (expression has type "None", variable has type "int")

and this is what i mean by secret intrinsic behavior. ThisType looks like it's implemented in the language, but if you attempt to recreate it, its magic disappears:

interface Foo {
    a: () => void
    foo: number
}

interface Bar {
    a: () => void
    bar: number
}

interface ThisType2<T> { }

type Baz = Foo & ThisType<Bar>
type Baz2 = Foo & ThisType2<Bar>

const foo: Baz = {
    foo: 1,
    a() {
        this.bar //no error
    }
}
const foo2: Baz2 = {
    foo: 1,
    a() {
        this.bar //Property 'bar' does not exist on type 'Foo & ThisType2<Bar>'.(2339)
    }
}

i thought maybe ThisType is augmenting an interface that's already defined elsewhere? but as far as i can tell it isn't

@KotlinIsland
Copy link

KotlinIsland commented Nov 17, 2021

Union[None] isn't an empty union, it's a union of NoneType, an empty union would be something like Union[] which is a syntax error.

Python type machinery (like Union) converts None to NoneType(the builtins.type of None).

And the Union factory checks if there is only one argument, and in that case just returns that argument, not a union of that argument.

So despite how it might read, Union[None] returns builtins.NoneType

@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Nov 18, 2021
@RyanCavanaugh
Copy link
Member

I don't think this would be problematic, but it seems like the easier fix would be to just put a comment above the declaration, since neither declaration form is going to tell you what's going on.

Why is everyone talking about Python unions? πŸ˜…

@KotlinIsland
Copy link

Because python's typing is cringe, not based. And this particular definition looks like something straight outta python

@yume-chan
Copy link
Contributor

I guess intrinsic tells users there is magic within, at least it's not a empty interface (which is any).

It's just a very small thing that can but not must be done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants