TypeScript
TypeScript copied to clipboard
Type annotations for default export
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!
Would not this be sufficient?
const config: webpack.Configuration = {
}
export default config;
Yes, that's what I do know but I wish I didn't have to.
related to https://github.com/Microsoft/TypeScript/issues/3792. we have tried to keep the module export as simple as possible on the syntax side.
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.
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!
import * as webpack from 'webpack'
export default {
...
} as webpack.Configuration
@IgorGee That's the proper way of doing it IMHO
The problem with
export default ... as X
is that it's a cast, so it purposely loses type safety.
@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
@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.
@IgorGee That's forbidden in @typescript-eslint/recommended.
const as = <T>(value: T) => value
export default as<webpack.Configuration>({
// ...
})
Would love to see this feature for nice succinct code
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.
I have the same problem too. Need to type default export instead of cast.
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
})();
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>
})
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?
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.
what about: ~~export default <MyType>{ prop: 'value' };~~ ?
Edited: no a good solution, it's a reverse assertion, it's even worst than as MyType.
what about:
export default <MyType>{ prop: 'value', };?
Great way, works for me, save me some typing
what about:
export default <MyType>{ prop: 'value', };?
interface MyType {
prop: string;
prop2: string;
}
const toExport: MyType = {
prop: "value",
}
export default <MyType>{
prop: "value",
};
in this example TS fails on toExport variable (Property 'prop2' is missing...) and passes on export default <MyType>{...}, but should fail too
what about:
export default <MyType>{ prop: 'value', };?
It is not a type declaration but a type assertion.
In other words, literally the same as as MyType, just another syntax. It was already discussed and discouraged here.
@richard-ejem I fully disagree, it's not a type assertion:
interface MyType {
hello: string;
}
export default <MyType>{ hello: false }; // 👉 Type Error
export default <MyType>{ hello: 'world!' }; // 👉 Type Checked
Unless… you're right… it's a reversed assertion. It's even worst because in the case of missing props, there is no error whatsoever, but with as MyType there is still a type error.
@zaverden that's indeed strange… It looks like an in-between check
@richard-ejem I fully disagree, it's not a type assertion:
interface MyType { hello: string; } export default <MyType>{ hello: false }; // 👉 Type Error export default <MyType>{ hello: 'world!' }; // 👉 Type Checked
Yes, it is an assertion, replace it with as and you get the same result.
interface MyType {
hello: string;
}
export default { hello: false } as MyType; // 👉 Type Error
export default { hello: 'world!' } as MyType; // 👉 Type Checked
The type error is there because assertions between completely incompatible types are illegal, please read the TS error message you get.
see https://www.tutorialsteacher.com/typescript/type-assertion , <TYPE> is old syntax for assertions, now rarely used because its syntax conflicts with JSX.
@richard-ejem Yes, I edited my initial comment to reflect that it's not a valid notation 😉 Thanks.
I think using semantics export default: Type Expression might add some restrictions, for instance, array definition with spaces between type and []. Using spaces between type and [] is allowed to define array type
const a: number [];
How this case should be handled in export default? Should spaces between type and [] be forbidden? For the following example I expect an error that array is not assignable to number;
export default: number [];
It seems that the main concern with using the as operator is that it simply casts to a type without checking for type compatibility.
There is a draft proposal for adding the new satisfies operator that might fix this issue - https://github.com/microsoft/TypeScript/pull/46827.
It allows checking type like so
interface Foo {
a: number;
}
export default {} satisfies Foo;
^^^
Type '{}' does not satisfy the expected type 'Foo'.
Property 'a' is missing in type '{}' but required in type 'Foo'.(1360)
@RyanCavanaugh @DanielRosenwasser What do you think? Can we add this case to https://github.com/microsoft/TypeScript/pull/46827 proposal use cases?
@a-tarasyuk I don't think syntax is ambiguous.
export default: number[];
// same as
type Default = number[];
export default Default;
export default: number[] = [];
// same as
const Default: number[] = [];
export default Default;
I am confusing about this too. any suggestion about typing the default export function?