-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Type and module imports of the same name shouldn't fail with "duplicate identifier" #48764
Comments
This is the intended behavior; export class Foo { } import type { Foo } from "./other";
// Legal to use value meaning of Foo (in type contexts)
function fn(x: typeof Foo) { } You're allowed to "shadow" |
I didn't know that about Would it be possible to check what Specifically my use case is to import a module that represents a type and its operators as both a namespace ( import * as Task from 'fp-ts/Task' Ideally per this issue we could do: import type { Task } from 'fp-ts/Task' Right now at best you can redefine the type: type Task<A> = Task.Task<A> Or worse yet carry the redundant namespace around with you everywhere: declare const x: Task.Task<A> |
We don't want to create a forward-versioning hazard in the referenced module. In other words, adding a value meaning to an identifier in your module should not possibly break people who refer to that module. |
@RyanCavanaugh can you elaborate how adding a value meaning later could break existing code? is that only the case because the reason i'm asking: i was looking for a solution to exactly the same problem @samh describes. right now i have two separate imports for half a dozen modules in almost every file, and it's starting to annoy me so much that i earnestly consider to rename all my types to import * as either from "@lib/either";
const foo:either.T<A,B> = either.left(...); instead of import { Either } from "@lib/either";
import * as either from "@lib/either";
const foo:Either<A,B> = either.left(...); |
Sure, let's say you write // other.ts
export interface Foo { } // another.ts
export const a = 0; // myFile.ts
import type { Foo } from "./other";
import * as Foo from './another'; This is the thing that you want to be OK. But if we did, then if you updated other.ts // other.ts
- export interface Foo { }
+ export class Foo { } Now |
so if |
Seconding @ritschwumm I've never seen |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
it seems the meaning of |
@RyanCavanaugh I'm unsure what to do regarding the closure of this ticket because this is still a pain point regardless of how it could potentially be solved. Should I raise a new ticket? |
I agree, it would be great if there was a good solution to this problem. You cannot do this: // Point.ts
export type Point = { x: number, y: number };
export add = (a: Point, b: Point): Point => ({ x: a.x + b.x, x: a.y + b.y });
// index.ts
import type ( Point } from "./Point";
import * as Point from "./Point";
const a: Point = { x: 2, y: 4 };
const b: Point = { x: 2, y: 4 };
const c = Point.add(a, b);
console.log(c); // { x: 4, y: 8 }; The only workaround that I have been able to find is this: export type Point = { x: number, y: number };
export const Point = { add: (a: Point, b: Point): Point => ({ x: a.x + b.x, x: a.y + b.y }) };
// index.ts
import ( Point } from "./Point";
const a: Point = { x: 2, y: 4 };
const b: Point = { x: 2, y: 4 };
const c = Point.add(a, b);
console.log(c); // { x: 4, y: 8 }; However, this is ugly and also has some significant differences. (1) You now cannot import a single function, and must import the entire object. I guess this could be fixed by exporting and function and an object that contains the function, as below: // You cannot do this:
import ( Point, add } from "./Point";
// Unless you export twice:
export type Point = { x: number, y: number };
export add = (a: Point, b: Point): Point => ({ x: a.x + b.x, x: a.y + b.y });
export const Point = { add }; (2) You get the error export type Point = { x: number; y: number };
export const Point = {
zero: (): Point => ({ x: 0, y: 0 }),
add: (a: Point, b: Point, c = Point.zero()): Point => {
c.x = a.x + b.x;
c.y = a.x + b.y;
return c;
},
}; (3) It can also cause problems when attempting to tree-shake the module. However, in fairness, most JavaScript optimizers are now able to identify and hoist the exported object. TLDR This pattern is very common in FP oriented libraries (gcanti/fp-ts#1003, gcanti/fp-ts#1044, gcanti/fp-ts#1415, gcanti/fp-ts#1633). |
@samhh @scottwillmoore Please help support the latest issue on this before it gets closed again π |
Here is my ideal syntax... @RyanCavanaugh I don't think this should have been closed, even if it is "working as intended." It's bizarre that type/namespace/function merging is supported within a file, but not via imports and exports. |
Bug Report
π Search Terms
π Version & Regression Information
This is the behavior in every version I tried, and I reviewed the FAQ for entries about
import type
.β― Playground Link
I don't think this is possible to demonstrate in the playground as it needs a module to
import *
from?π» Code
π Actual behavior
π Expected behavior
The compiler should recognise that one identifier is a type and the other a value and allow the two to coexist, as it does in other scenarios.
The text was updated successfully, but these errors were encountered: