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

Type annotations for default export #13626

Open
mohsen1 opened this issue Jan 23, 2017 · 51 comments
Open

Type annotations for default export #13626

mohsen1 opened this issue Jan 23, 2017 · 51 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@mohsen1
Copy link
Contributor

mohsen1 commented Jan 23, 2017

TypeScript Version: 2.1.1

Code

import * as webpack from 'webpack';
export default: webpack.Configuration {
};

Expected behavior:
No error

Actual behavior:

[ts] Expression expected. error at default:

I couldn't find an issue for this but it's very likely it's a duplicate


Please 👍 on this issue if you want to see this feature in TypeScript and avoid adding "me too" comments. Thank you!
@mhegazy
Copy link
Contributor

mhegazy commented Jan 23, 2017

Would not this be sufficient?

const config: webpack.Configuration  = { 

}
export default config;

@mohsen1
Copy link
Contributor Author

mohsen1 commented Jan 23, 2017

Yes, that's what I do know but I wish I didn't have to.

@mhegazy
Copy link
Contributor

mhegazy commented Jan 23, 2017

related to #3792. we have tried to keep the module export as simple as possible on the syntax side.

@mhegazy mhegazy added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Jan 23, 2017
@RyanCavanaugh RyanCavanaugh added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature and removed In Discussion Not yet reached consensus labels Jan 24, 2017
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jan 24, 2017

I think it's a little more related to to the following scenario regarding function declarations.

if I want to write a decorator that is verified against the PropertyDecorator type in lib.d.ts, I can't write it easily. I have to use a function expression.

export let Encrypt: PropertyDecorator = function (obj, propName) {
};

which is case of friction whenever I want to actually implement a decorator without making a mistake.

@barakbd
Copy link

barakbd commented Feb 20, 2018

I am trying to use a default export in the following way

readdirSync(join(__dirname, "../controllers/box"))

  .filter(f => f !== "*.spec.ts")
  .forEach(controllerFile => {
    const controllerBaseName = basename(controllerFile, ".js")
    import(`../controllers/box/${controllerBaseName}`).then((controller)=>{
      appRouter.use(
        `/box/${controllerBaseName}`, controller.default(boxServiceAccountClient))
    }).catch(error=>{
      console.log(error)
    })
  }); //end forEach

I get an error:

TypeError: controller.default is not a function at fs_1.readdirSync.filter.forEach.Promise.resolve.then.then.controller (/Users/bbendavi/Documents/cdt-box/dist/server/config/routes.js:16:71) at <anonymous> at process._tickCallback (internal/process/next_tick.js:160:7) at Function.Module.runMain (module.js:703:11) at startup (bootstrap_node.js:193:16) at bootstrap_node.js:617:3

I asked this on SO with a full file examples:
https://stackoverflow.com/questions/48696327/how-to-define-a-module-default-export-type-in-typescript

Would much appreciate your help!

@IgorGee
Copy link

IgorGee commented Mar 10, 2019

import * as webpack from 'webpack'

export default {
...
} as webpack.Configuration

@jlouazel
Copy link

jlouazel commented Apr 1, 2019

@IgorGee That's the proper way of doing it IMHO

@pelotom
Copy link

pelotom commented Apr 1, 2019

The problem with

export default ... as X

is that it's a cast, so it purposely loses type safety.

@jlouazel
Copy link

jlouazel commented Apr 1, 2019

@pelotom As a general matter of fact, you're completely right, I'd avoid putting this in my codebase. For the scope of this thread where this is about webpack configuration, I think we're just fine

@pelotom
Copy link

pelotom commented Apr 1, 2019

@jlouazel leaving aside whether type safety is any less important in a webpack config... the issue is not specific to webpack, it's about annotating the type of the default export in general.

@oli-laban
Copy link

@IgorGee That's forbidden in @typescript-eslint/recommended.

@yarnaimo
Copy link

const as = <T>(value: T) => value

export default as<webpack.Configuration>({
    // ...
})

@thethomaseffect
Copy link

Would love to see this feature for nice succinct code

@mccallofthewild
Copy link

mccallofthewild commented Mar 15, 2020

If you're exporting a function, put it in parenthesis before the as.
e.g.

export default ((req, res) => {
   // Intellisense Enabled on `req` & `res`!
   return 'Hello World!';
}) as RequestHandler;

🚩Edit for downvoters: Typescript does check functions for return type & parameter compatibility when typecasting. Unlike typecasting for object types, functions retain a degree of type safety.

@Mukhametvaleev
Copy link

Mukhametvaleev commented Mar 16, 2020

I have the same problem too. Need to type default export instead of cast.

@white-room
Copy link

could also use an iife so that the type is at the beginning of the export rather than the end

export default ((): MyType => ({
  k: v
})();

@ackvf
Copy link

ackvf commented Mar 28, 2020

While this gives me type hints inside the function (thanks @mccallofthewild )

export default (({ withIcon, children }) => {
  return <SomeJSX withIcon={withIcon}>{children}</SomeJSX>
}) as React.FC<{withIcon: boolean}>

I would still prefer to have the type declared up front (not sure about the syntax here though)

export default: React.FC<{withIcon: boolean}> (({ withIcon, children }) => {
  return <SomeJSX withIcon={withIcon}>{children}</SomeJSX>
})

@LongTengDao
Copy link
Contributor

LongTengDao commented Oct 22, 2020

Maybe export const default :Type = value; export type default = Type; export interface default {} could bring us more uniformity, avoid to introduce a new set of grammars just for default?

@soullivaneuh
Copy link

soullivaneuh commented Dec 21, 2020

The solution proposed by @IgorGee works but is not fully reliable for type checking.

My default export is a ResourceProps where the property name is required.

If I try with a typed constant (the actual only solution):

const resourceProps: ResourceProps = {};
export default resourceProps;

I will have:

Property 'name' is missing in type '{}' but required in type 'ResourceProps'.  TS2741

But nothing if I do it like this:

export default {} as ResourceProps;

This make things harder to debug if something goes wrong because of this missing property. And we are using Typescript for that, right? :-)

However, I also understand the need to type the default export. On some cases, we just return a configuration object like that:

const resourceProps: ResourceProps = {
  name:"users",
  icon: UserIcon,
  list: UserList,
  create: UserCreate,
  edit: UserEdit,
  options:{
    label: 'Utilisateurs',
  },
};

export default resourceProps;

It's a bit cumbersome to be forced to declare a new variable just to take benefit of typing.

That was my two cents. I wanted to illustrate with some samples because I was myself a bit confused as a TypeScript beginner when I saw the lot of down votes on the previous solutions.

@francois-gsk
Copy link

francois-gsk commented Oct 5, 2021

what about: export default <MyType>{ prop: 'value' }; ?

Edited: no a good solution, it's a reverse assertion, it's even worst than as MyType.

@bingtsingw
Copy link

bingtsingw commented Nov 17, 2021

what about:

export default <MyType>{
  prop: 'value',
};

?

Great way, works for me, save me some typing

@shtse8
Copy link

shtse8 commented Jun 23, 2022

I am confusing about this too. any suggestion about typing the default export function?

@dobesv
Copy link

dobesv commented Jul 18, 2022

I am confusing about this too. any suggestion about typing the default export function?

Currently you have to put it into a const first, then export it:

const dftExport: SomeType = { ... };
export default dftExport;

This issue is tracking a request to implement some way to avoid the extra repetition.

@dobesv
Copy link

dobesv commented Jul 18, 2022

The use case I'm looking at for this is to type and name a function that's a default export, without repeating the function name:

export interface MyComponentProps {
   yes: boolean;
}
export default function MyComponent(props) {

} satisfies SFC<MyComponentProps>;

@jchook
Copy link

jchook commented Jul 19, 2022

@ackvf's syntax suggestion seems like the best proposed so far.

export default: React.FC<{withIcon: boolean}> (({ withIcon, children }) => {
  return <SomeJSX withIcon={withIcon}>{children}</SomeJSX>
})

Would that be difficult to implement?

export default: {type} {value}

@dobesv
Copy link

dobesv commented Jul 20, 2022

I guess another option could be to allow "default" as a variable name to export.

export const default: React.FC<MyProps> = function MyComponent(props) { ... }

@NikolaRHristov
Copy link

NikolaRHristov commented Aug 27, 2022

This is type checked:

import type { Config } from "package";

export default (): Config => {
	return {
		properties: true
	};
};

@soullivaneuh
Copy link

@NikolaRHristov Yes, but this works only if you export a function, you are typing its return type.

@sidvishnoi
Copy link

Re: #13626 (comment)

I think this works well enough with satisfies?

type MyType = { name: string }

export default { name: 5 } satisfies MyType;
// Type 'number' is not assignable to type 'string'.(2322)

export default { name: "world", foo: 4 } satisfies MyType;
// Object literal may only specify known properties, and 'foo' does not exist in type 'MyType'.(1360)

export default { name: "world" } satisfies MyType;
// OK

@dobesv
Copy link

dobesv commented Jan 30, 2023

Will satisfies allow typescript to infer argument and return types in something like the following?

export interface MyComponentProps { foo: string }
export default function MyComponent(props) {  return <> ... </>; }  satisfies React.SFC<MyComponentProps>;

@sidvishnoi
Copy link

@dobesv #13626 (comment)

type RequestHandler = (req: string, res: number) => string;

export default ((req, res) => {
   return 1;
}) as RequestHandler;
// Conversion of type '(req: string, res: number) => number' to type 'RequestHandler' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
//   Type 'number' is not comparable to type 'string'.(2352)
// and the whole block is highlighted as error.

export default ((req, res) => {
   return 1;
}) satisfies RequestHandler;
// Type '(req: string, res: number) => number' does not satisfy the expected type 'RequestHandler'.
//  Type 'number' is not assignable to type 'string'.(1360)
// but "RequestHandler" is highlighted as error (I would expect the return value to be highlighted).

Playground Link

@dobesv
Copy link

dobesv commented Jan 31, 2023

I think that satisfies this request to my satisfaction, in that case.

@mon-jai
Copy link

mon-jai commented Jan 31, 2023

@sid Vishnoi It works on function declarations too.

export default (function (req, res)  {
   return 1;
}) satisfies RequestHandler;

Playground Link

@dragomirtitian
Copy link
Contributor

dragomirtitian commented Mar 16, 2023

Just want to point out that using both as and satisfies might actually be the best solution here until we get actual syntax. satisfies checks that the value conforms to the type, as forces the type instead of allowing TS infer its own type

type Union = { v: 1 | 2 }
export default { v: 1 } satisfies Union as Union; // Checked against Union and Union is preserved as the type of the export.

Playground Link

Thanks to @acutmore for suggesting this use of satisfies T as T

@rampion
Copy link

rampion commented Jun 30, 2023

Another workaround is to pass the export value to an identity function to check its value:

function verify<T>(t: T): T { return t }

export default verify<React.FC<IProps>>(function(props) {
  // ...
})

I'm not sure if there's a canonical identity function that'd avoid introducing your own.

@ekcom
Copy link

ekcom commented Jul 25, 2023

For now, we continue to export default exportDefault; with something like const exportDefault: webpack.Configuration = {}; .

  • as induces type coercion
  • satisfies is a type assertion. It does not type the variable (even though it will validate it)

@dobesv
Copy link

dobesv commented Jul 25, 2023

satisfies is a type assertion. It does not type the variable

Oh, too bad. I thought an earlier commenter said that it did type it. We haven't upgraded to the version of typescript with this yet, but this was one thing I was looking forward to.

@mccallofthewild
Copy link

You can also declare a typed variable and assign it in your export.

type Union = { v: 1 | 2 };

let d: Union;

export default d = {
  v: 1,
};

@mon-jai
Copy link

mon-jai commented Oct 4, 2023

@mccallofthewild Then it could be reassigned.

@a-tarasyuk
Copy link
Contributor

@RyanCavanaugh What are your thoughts on including this issue in the [Feature Updates] discussion list?

@mccallofthewild
Copy link

@mon-jai No. 😁 That's not how assignment works.

let a;
let b = a = 1;
a = 2;
console.log(b == a);
// `false`

@mon-jai
Copy link

mon-jai commented Oct 5, 2023

Shouldn't the following code work?

// d.js
let d: number;
export default d = 1:

// Other files
import d from "d.js";
d = 2;

@MacroMan
Copy link

satisfies T as T doesn't reduce boilerplate in all circumstances. eg using a type from a value doesn't work at all:

export default {some:'value'} satisfies SomeType<typeof SomeValue> as SomeType<typeof SomeValue>

and even if it did work, I'm repeating my type declaration twice.

Doing export default: SomeType<typeof SomeValue> {some:'value'} would be great for reducing boilerplate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.