Open mohsen1 opened 7 years ago
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?
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.
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>;
@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}
I guess another option could be to allow "default" as a variable name to export.
export const default: React.FC<MyProps> = function MyComponent(props) { ... }
This is type checked:
import type { Config } from "package";
export default (): Config => {
return {
properties: true
};
};
@NikolaRHristov Yes, but this works only if you export a function, you are typing its return type.
Re: https://github.com/microsoft/TypeScript/issues/13626#issuecomment-478648204
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
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>;
@dobesv https://github.com/microsoft/TypeScript/issues/13626#issuecomment-1409060244
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).
I think that satisfies
this request to my satisfaction, in that case.
@Sid Vishnoi It works on function declarations too.
export default (function (req, res) {
return 1;
}) satisfies RequestHandler;
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.
Thanks to @acutmore for suggesting this use of satisfies T as T
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.
For now, we continue to export default exportDefault;
with something like const exportDefault: webpack.Configuration = {};
.
as
induces type coercionsatisfies
is a type assertion. It does not type the variable (even though it will validate it)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.
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,
};
@mccallofthewild Then it could be reassigned.
@mon-jai No. ๐ That's not how assignment works.
let a;
let b = a = 1;
a = 2;
console.log(b == a);
// `false`
Shouldn't the following code work?
// d.js
let d: number;
export default d = 1:
// Other files
import d from "d.js";
d = 2;
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
TypeScript Version: 2.1.1
Code
Expected behavior: No error
Actual behavior:
[ts] Expression expected.
error atdefault:
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!