Comments (11)
You have two modules shapes here:
- The one from
a
, which has afoo: number
property - The one from
a.test
, which has either nothing, nothing, or adefault: {}
property
The "nothing" variants supertype the { foo: number }
property, so the union import(...) | import(...)
gets reduced to the more-general of the two (nothing)
from typescript.
But if a.test
don't have export statement, I think it should be something like Record<string, never>
.
Because the only way to let ESM module have X
property, is using export
statement.
If there is zero export statement, it means a zero key object Record<string, never>
.
Right?
Change to a.test.mts
-
still get
typeof import("/path/to/reproduce/a.test")
-
instead of something like
{foo: 42} | Record<string, never>
.
from typescript.
That would be a very bad type; given x: Record<string, never>
you can write nonsense like Math.sin(x.foo)
without error
from typescript.
Quote recommendation from @typescript-eslint/ban-types
If you want a type meaning "empty object", you probably want
Record<string, never>
instead.
from typescript.
The TypeScript maintainers have a... let's say... contentious relationship with the ban-types
rule, specifically the part about it banning {}
Either way, Record<string, never>
doesn't mean "empty object", regardless of what eslint recommends. It actually means "object where accessing any property throws an exception". Reading a property from an empty object doesn't give you a value of type never
(i.e. an exception), it gives you undefined
.
from typescript.
But, compared with {}
which means any-alike, Record<string, never>
seems to be yet the most correct type, to describe empty-object, maybe say always-zero-key-object, which describe an ESM module without any export statement.
from typescript.
If you want a type meaning "empty object", you probably want Record<string, never> instead.
The typescript-eslint maintainers are uncharacteristically super wrong about this. { }
is a valid type with a valid meaning and it's just wrong for them to ban it. Unfortunately we haven't had much luck changing their minds.
from typescript.
👋 typescript-eslint maintainer here. The ban-types
rule's default options were formed many years ago when TypeScript had less fleshed out rules around {}
, object
, and related. If there's evidence that the rule's options are now and/or always were wrong about something, we'd happily take an issue to improve them. The dev lead for TypeScript saying the rule is super wrong is pretty compelling evidence. 😄
Also looking at typescript-eslint/typescript-eslint#5018 (comment):
There's just a lot of confusion likely to come down the road on this specific ban in general, since we're likely to change the definition of NonNullable to T & { } in 4.8, and are already in 4.8 going to change the default narrowing rules such that a truthy narrowing of a value of an unconstrained type parameter becomes T & { }. Having typescript-eslint, out of the box, ban you from writing a type that TypeScript itself is inferring under totally normal operation, is awkward.
TypeScript did in fact ship the change to make type NonNullable<T> = T & {};
in #49119 - along with a host of other {}
improvements. I'd wager that there's even more evidence we should revisit the ban.
Three related issues on our side:
- typescript-eslint/typescript-eslint#5947: the most recent example I know of around making the rule move away from
Record<string, never>
- typescript-eslint/typescript-eslint#8697: sparked by Ryan generally referencing
ban-types
and{}
vs.Record<string, never>
- typescript-eslint/typescript-eslint#8700: sparked by this discussion too
For context: typescript-eslint is a separate maintenance team from TypeScript. Our opinions can sometimes fall out of sync with TypeScript best practices because we -like many independent open source projects- generally only take action in response to user prodding. We haven't been prodded about ban-types
(that I know of) in quite a while. If there's anything ban-types
or any other rule is doing that's out of sync with how TypeScript is meant to be, and TypeScript's handling of that rule's area has changed since the last time that rule was discussed, we'd happily take an issue prodding us to change. ❤️
from typescript.
As far as I see, maybe should split empty object type
into another issue, if needed, as there are now two issue?
The original issue is
Actual
ESM X.test
which have zero export statement, is actually super type of ESM X
which have at least one export statement
Expected
ESM X.test
which have zero export statement, should be sub type of ESM X
which have at least one export statement.
Because it is
-
zero-key-object-at-read
-
not
zero-key-object-at-write
, but consider as nop
> mod = await import('/tmp/empty-file.mjs')
> mod.x=1
1
> mod.x
undefined
from typescript.
There's no type in TypeScript that means "a type that is guaranteed to be completely empty" because TypeScript doesn't have sealed/exact types
from typescript.
This issue has been marked as "Not a Defect" and has seen no recent activity. It has been automatically closed for house-keeping purposes.
from typescript.
Related Issues (20)
- [NewErrors] 5.5.0-dev.20240421 vs 5.4.5 HOT 63
- [ServerErrors][TypeScript] 5.5.0-dev.20240421 HOT 14
- [ServerErrors][JavaScript] 5.5.0-dev.20240421 HOT 8
- `T extends infer I1 extends { K: infer I2 }` infers I2 to unknown HOT 6
- Wrong type narrowing on union type. HOT 2
- Document link for reference types and path
- object with `?:` property type, spread last --> allowed (but shouldn't) HOT 3
- Add a flag to require JS object literals to be initialized with all declared members HOT 4
- Type inference lost after spreading array with `ArrayLike` HOT 1
- Regular Expression finds HOT 4
- "This comparison appears to be unintentional" and control flow analysis with a variable updated in lambda HOT 4
- Classes static generic function return `any` type. HOT 2
- Is assignment not allowed here? HOT 2
- Editor changes overload resolution based on syntax HOT 5
- TS cache corruption leads to "error TS2590: Expression produces a union type that is too complex to represent" HOT 1
- Type alias circularly references itself (5.4 regression) HOT 1
- error TS2385: Overload signatures must all be public, private or protected. HOT 2
- `export type * ...` statements in `.d.ts` files do not work (5.4 regression) HOT 4
- HTMLFormElement disallows symbol keys HOT 5
- TypeScript language service cannot find subclass references/implementation of mixin methods
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typescript.