I really like the ‘unknown’ type. In catch statements for example the error is unknown but you can do dynamic checks to see what it is, then typescript lets you treat it that way.
I really like the ‘unknown’ type. In catch statements for example the error is unknown but you can do dynamic checks to see what it is, then typescript lets you treat it that way.
One thing I love about typescript that I've never seen in other languages is that you can adjust the type checker exactly for the level of rigidness that project at hand needs. Any is okay for 200 LOC quick prototypes.
It’s also just flat out impossible in many other languages because the compiler actually needs to know the types for performance reasons.
Typescript managed to be the best of both worlds, as you can be both lenient when you're still in the design/prototyping phase of a project, and need to test idea quickly, but also add as much type-check as you see fit when you're maintaining a project, and need to interface with lots of existing code.
As such, I'm always a bit miffed when I see so many people adamant on setting it to maximum strictness by default, under the guise of "best practice".
Especially `strictNullChecks`. It's a massive headache to deal with that later on.
If you're writing a prototype (so the code will eventually be thrown away), it's just 200 LOC and you're using `any` for everything, why even use TypeScript? Should just use vanilla JavaScript instead.
And if you wanna break the rule of throwing away prototype code, you can always "upgrade" it to TypeScript later without too much fuzz.
The case where I've found this useful is making a quick and dirty prototype that integrates within an existing typescript codebase. Such a prototype would never make it to the main branch but sometimes it's useful to make a quick proof of concept of the the runtime behaviour before comitting to sorting out the types.
I love using TS for prototypes because you can define the interfaces wherever you feel they're important, and leave the rest to any.
And like you said, you can always improve the types later
You should not need ‘any’ in code written in TypeScript from the beginning.
Writing Typescript well requires some fundamentally different patterns than javascript. Adding types afterward is like pulling out your teeth slowly.
Lets say you have some input json that you want to slightly modify to something else. How would you do this with unknown? I can't just blindly replace any with unknown. I'd get errors like this: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'unknown'.ts(2407) For example, how can I do this better?
Remember the input json could be pretty much anything. I don't have a spec other than I only care about things that end with __c.
https://github.com/kusl/salesforcecontactmapper/blob/eff0b3e...
import { Output } from "./Output";
import { Preference } from "./Preference";
export function MyMap(input: unknown): Output {
const mypreferences = Array<Preference>();
for (const prefCode in input) {
if (prefCode.endsWith("__c")) {
if (prefCode === "IsInternalUpdate__c") {
continue;
}
let currentValue = "";
if (input[prefCode] !== null) {
currentValue = input[prefCode].toString();
}
if (currentValue === "true") {
currentValue = "True";
}
if (currentValue === "false") {
currentValue = "False";
}
const preference: Preference = {
PrefCode: prefCode,
CurrentValue: currentValue
}
mypreferences.push(preference);
}
}
const myOutput: Output = {
ContactId: input.Contact__c,
Email: input.ContactEmail__c,
IsInternalUpdate: true,
Preferences: mypreferences
}
return myOutput;
}You could say technically could be simply { "Unsubscribe__c": false } or even {} both of which are silly in my case because there is no key for me to identify who the person is but they are valid inputs.
Or the test case I have is https://github.com/kusl/salesforcecontactmapper/blob/eff0b3e...
Or the input could have a thousand key values and I only care about some of them. What should my object look like? How do I create a class that says everything that ends in "__c" is something I care about? I tried unknown. I tried Object. How do I fix this (and learn something so I fix all future code I write)?
If you wanna go down that rabbit hole, I'd suggest Typescript type challenges. Completely blew my mind when I came across it the first time.
When you start out with typescript you might think Omit<> and Partial<> are cool but holy hell, you can do so much more.
input: Record<string, unknown> const myOutput: Output = {
ContactId: input.Contact__c,
Email: input.ContactEmail__c,
IsInternalUpdate: true,
Preferences: mypreferences
}
(property) Output.ContactId: string Type 'unknown' is not assignable to type 'string'.ts(2322) Output.ts(4, 5): The expected type comes from property 'ContactId' which is declared here on type 'Output' if (input[prefCode] !== null) {
currentValue = input[prefCode].toString();
}
in the lines above, I see a red underline under input[prefCode]> Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Object'. No index signature with a parameter of type 'string' was found on type 'Object'.ts(7053)
function getProperty<T, K extends keyof T>(o: T, propertyName: K): T[K] {
return o[propertyName];
}
?A value whose type we don't know is different from a value which we know could be any type.
unknown = I don’t know
any = I don’t care
Often the implications are the same, but as you can intuit, any is a lot more relaxed.
Any will work anywhere because you just don’t care what the type is.
Unknown is stricter. It won’t work in cases where other types expect you to know what type you’re dealing with.
The compiler treats unknown very cautiously, whereas you can slap an any on pretty much anything.
Whereas ‘Unknown’ might be a stepping stone to introduce a more specific type once all the use cases are known. I let it handle strings and integers, log a line about any other type that calls it, and continue behaviour as it is today.
For example:
function handleString(s: string) {
//...
}
declare const a: any;
declare const u: unknown;
handleString(a); // works with zero intervention
handleString(u); // error: Argument of type 'unknown' is not assignable to parameter of type 'string'
handleString(u as string); // works again with explicit re-typing