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

F.getWithDefault Coercion type problem #84

Open
mattaiod opened this issue Jul 12, 2023 · 5 comments
Open

F.getWithDefault Coercion type problem #84

mattaiod opened this issue Jul 12, 2023 · 5 comments

Comments

@mattaiod
Copy link

mattaiod commented Jul 12, 2023

Hello everyone, first : thanks for this incredible library, look like the best in functionnal programmin in Ts

In the basic example

const res = pipe(
  [1, 2, 3, 4, 5], // → [1, 2, 3, 4, 5]
  A.dropExactly(2), // → Some([3, 4, 5])
  O.flatMap(A.head), // → Some(3)
  O.map(N.multiply(10)), // → Some(30)
) // → 30

O.getWithDefault(0)(res) // → 30

I have an typescript error at the last line:

Argument of type 'Option<number>' is not assignable to parameter of type 'Option<0>'.
  Type 'number' is not assignable to type 'Option<0>'.ts(2345)

The only way I have to fix that is to explicit precise the type like:
O.getWithDefault(0 as number)(res) // → 30

The version not curried works:
O.getWithDefault(res, 0)

I guess it's not normal so what is the reason and the solution?

Here my .eslintrc :

{
"extends": "@antfu",
"plugins": [
"functional"
],
"rules": {
"@typescript-eslint/quotes": 0,
"no-console": "off",
"array-callback-return": "error",
"no-constructor-return": "error",
"no-duplicate-imports": "off",
"no-new-native-nonconstructor": "error",
"no-self-compare": "error",
"no-template-curly-in-string": "error",
"no-unused-private-class-members": "error",
"class-methods-use-this": "error",
"consistent-return": "error",
"default-case": "error",
"dot-notation": "error",
"eqeqeq": "error",
"init-declarations": "error",
"no-eq-null": "error",
"no-extend-native": "error",
"no-implicit-coercion": "error",
"@typescript-eslint/no-unused-vars": "off",
"no-implicit-globals": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-object": "error",
"no-var": "error",
"prefer-object-spread": "error",
"require-await": "error",
"yoda": "error",
"explicit-function-return-type": "off"
}
}

tsconfig.json:

{
"compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true,
"baseUrl": ".",
"module": "ESNext",
"target": "ESNext",
"lib": [
"DOM",
"ESNext",
"WebWorker"
],
"strict": true,
"esModuleInterop": true,
"jsx": "preserve",
"skipLibCheck": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"incremental": false,
"noImplicitOverride": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strictFunctionTypes": true,
"alwaysStrict": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"useUnknownInCatchVariables": true,
"allowUnusedLabels": false,
"allowUnreachableCode": false,
"noUncheckedIndexedAccess": true,
"noPropertyAccessFromIndexSignature": true,
"noFallthroughCasesInSwitch": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"allowJs": false,
"forceConsistentCasingInFileNames": true,
"types": [
"vitest",
"vite/client",
"vue/ref-macros",
"vite-plugin-pages/client",
"vite-plugin-vue-layouts/client",
],
"paths": {
"~/": [
"src/
"
]
}
},
"exclude": [
"dist",
"node_modules",
"cypress",
"auto-imports.d.ts"
]
}

Thank you in advance for your attention

@mattaiod mattaiod changed the title Coercion type problem F.getWithDefault Coercion type problem Jul 12, 2023
@JUSTIVE
Copy link
Sponsor

JUSTIVE commented Jul 19, 2023

give type parameter to O.getWithDefault.
like

O.getWithDefault<number>(0).

@mattaiod
Copy link
Author

@JUSTIVE Yes, I'm familiar with this solution, but I'd like to know if there's a way to improve this function to have this default behavior.

@probablykabari
Copy link

@mattaiod you're issue above is because O.getWithDefault expects 0 to be an Option<number> type but it is a literal integer, take a look at the signature for why the non-curried version works:

export declare function getWithDefault<A>(option: Option<A>, defaultValue: NonNullable<A>): A;
export declare function getWithDefault<A>(defaultValue: NonNullable<A>): (option: Option<A>) => A;

As you see, the function would explicitly type A as 0 in the curried version whereas it would resolve to just number in the other, because normally the curried value would be within the pipe already and that would define the type. Basically don't use the curried version outside of a pipe/flow sequence OR type it yourself.

Note, there are a lot of instances where you need to do this when using getWithDefault. For example, the following would be a type error because the default is never[]

pipe(O.fromNullable([1, 2, 3]), O.getWithDefault([]))

@JUSTIVE
Copy link
Sponsor

JUSTIVE commented Feb 11, 2024

Maybe the NoInfer utility type from the Typescript 5.4.0 would be solution for this.
updated example above, seems just working as intended

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants