Skip to content

Declarations and "Exported variable 'XXX' has or is using name 'YYY' from external module "ZZZ" but cannot be named." #24666

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

Closed
jamiewinder opened this issue Jun 4, 2018 · 5 comments
Assignees
Labels
Bug A bug in TypeScript Domain: Declaration Emit The issue relates to the emission of d.ts files Fixed A PR has been merged for this issue

Comments

@jamiewinder
Copy link

I was pleased to see #9944 had been closed with what looked like a feature of 2.9 'fixing' the issue. However, it still doesn't seem to be working as I'd expect. Take this example:

import styled from "styled-components";

export const Container = styled.div`
    background: red;
`;

With a normal TypeScript project, using the tsconfig default except enabling declarations. I get three errors:

src/styles.ts(3,14): error TS4023: Exported variable 'Container' has or is using name 'React.ClassAttributes' from external module "/node_modules/@types/react/index" but cannot be named.
src/styles.ts(3,14): error TS4023: Exported variable 'Container' has or is using name 'React.HTMLAttributes' from external module "/node_modules/@types/react/index" but cannot be named.
src/styles.ts(3,14): error TS4023: Exported variable 'Container' has or is using name 'StyledComponentClass' from external module "/node_modules/styled-components/typings/styled-components" but cannot be named.

The obvious solution seems to be importing the types involved:

import styled, { StyledComponentClass } from "styled-components";
import * as React from "react";

export const Container = styled.div`
    background: red;
`;

Which would be fine, I suppose (though I don't see why it is necessary..?) but this doesn't work with noUnusedLocals as you get more errors:

src/styles.ts(1,18): error TS6133: 'StyledComponentClass' is declared but its value is never read.
src/styles.ts(2,13): error TS6133: 'React' is declared but its value is never read.

... However you can work around that by explicitly using the types, but even with this basic example it gets absurdly verbose:

import styled, { StyledComponentClass } from "styled-components";
import * as React from 'react';

export const Container: StyledComponentClass<React.ClassAttributes<HTMLDivElement> & React.HTMLAttributes<HTMLDivElement>, any, React.ClassAttributes<HTMLDivElement> & React.HTMLAttributes<HTMLDivElement>> = styled.div`
    background: red;
`;

What is the recommended solution / workaround to this?

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Jun 5, 2018

I can't find the comment I made on a workaround anymore. It was on a similar issue but even worse, because when this happens with generic types, you generally find yourself losing out on type inference.

The workaround was to use the imported type with a dummy variable if it's an interface or type alias.

If it's a class, you can get away with NameOfClass; as a statement and it counts as a usage.

With interfaces and the like,

import {SomeGenericInterface} from "thing";

const dummy : SomeGenericInterface<any, any, any>|undefined = undefined;
dummy;

I'm on my phone so I'm not sure if I've described it properly.


This way, type inference still works and you've imported and used the type. but it's extremely ugly and hacky. And, in general, you don't know what needs to be imported and used this way until you want to compile the code...

I wish there was a way for it to automatically import during the compile phase because this workaround I use has given me no problems.

It's just repeatedly,

  1. Run tsc
  2. See that error?
  3. Import type
  4. Create dummy variable of imported type union undefined
  5. GOTO 1

So, I've got it down to an algorithm but you might end up needing to do this for hundreds of files, to compile. And you don't need to do this at all with --noEmit or to run with ts-node

@jamiewinder
Copy link
Author

Thanks, @AnyhowStep - that's very similar to what I do at the moment too:

((_1?: StyledComponentClass<any, any>, _2?: typeof React) => 0)();

🤢

I've got dozens of files that start this way.

@AnyhowStep
Copy link
Contributor

I like your way better, actually.

@AnyhowStep
Copy link
Contributor

I just tried something and it seemed to work (I haven't exhaustively tested this so I'm not 100% sure)

Just do,

import * as package1 from "some-package-1";
import * as package2 from "some-package-2";
package1;
package2;

Assuming package1 andpackage2 contain interfaces/type aliases/classes, that you need for emitting. This helps if there are many interfaces/type aliases/classes in a package that are needed.

Doesn't help so much if you only need the one type.

@mhegazy mhegazy added Bug A bug in TypeScript Domain: Declaration Emit The issue relates to the emission of d.ts files labels Jun 13, 2018
@mhegazy mhegazy added this to the TypeScript 3.0 milestone Jun 13, 2018
@weswigham
Copy link
Member

weswigham commented Jun 14, 2018

@jamiewinder Thanks for the report! Apologies - looks like our visibility checking logic in declaration emit didn't handle things aliased via export= correctly (as is the case for the React declaration file), so is exhibiting the old behavior for now. I should have a fix up tomorrow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Domain: Declaration Emit The issue relates to the emission of d.ts files Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

4 participants