Skip to content

Instantly share code, notes, and snippets.

@gr2m
Last active November 10, 2020 20:20
Show Gist options
  • Save gr2m/09182ddd46e72672a8a8a30e34942988 to your computer and use it in GitHub Desktop.
Save gr2m/09182ddd46e72672a8a8a30e34942988 to your computer and use it in GitHub Desktop.

Update

The difference between local and play environment is strictNullChecks. It's set to false by default locally, but is enabled on the playground by default.

Here is the playground link with strictNullChecks set to false.

The fix was as simple as changing preview = undefined to preview = unknown. Which I could have sworn I tried and it didn't work but here we are 🤷‍♂️

Here is an insightful explanation by @andrewbranch

The way --strictNullChecks=false works is we remove undefined | null from the domain of every type by the time you observe it. So string | null | undefined becomes string. So what does undefined become when you remove undefined from its domain?

It becomes the empty union, aka never!

Original REadme

Test case for a weird problem with TypeScript.

Here is the playground link where the problem does not occur.

But to reproduce the problem locally:

git clone [email protected]:09182ddd46e72672a8a8a30e34942988.git gist-typescript-test
cd gist-typescript-test
npm install
npx tsc --noEmit --declaration --noUnusedLocals test.ts

Results in

test.ts:72:14 - error TS2322: Type '{}' is not assignable to type 'never'.

72 export const getApp: GetApp = {};
                ~~~~~~


Found 1 error.

For me, anyway.

When changing

-export const getApp: GetApp = {};
+export const getApp: GetApp = {
+  mediaType: {
+    previews: [],
+  },
+};

The error is

test.ts:74:5 - error TS2322: Type 'undefined[]' is not assignable to type 'never'.

74     previews: [],
       ~~~~~~~~

🤷‍♂️ halp

{
"name": "tmp.uR9v2E57",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Gregor Martynus (https://twitter.com/gr2m)",
"license": "MIT",
"dependencies": {
"typescript": "^4.0.5"
}
}
type paths = {
"/app": {
get: {
parameters: {};
responses: {};
};
};
"/codes_of_conduct": {
get: {
parameters: {};
responses: {};
};
};
};
// https://stackoverflow.com/a/50375286/206879
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type ExtractParameters<T> = "parameters" extends keyof T
? UnionToIntersection<
{
[K in keyof T["parameters"]]: T["parameters"][K];
}[keyof T["parameters"]]
>
: {};
type ExtractRequestBody<T> = "requestBody" extends keyof T
? "application/json" extends keyof T["requestBody"]
? T["requestBody"]["application/json"]
: {
data: {
[K in keyof T["requestBody"]]: T["requestBody"][K];
}[keyof T["requestBody"]];
}
: {};
type ToOctokitParameters<T> = ExtractParameters<T> & ExtractRequestBody<T>;
type RequiredPreview<T> = T extends string
? {
mediaType: {
previews: [T, ...string[]];
};
}
: {};
type Operation<
Url extends keyof paths,
Method extends keyof paths[Url],
Preview = undefined
> = {
parameters: ToOctokitParameters<paths[Url][Method]> &
(Preview extends string ? RequiredPreview<Preview> : {});
request: {};
response: unknown;
};
export interface Endpoints {
"GET /app": Operation<"/app", "get">;
"GET /codes_of_conduct": Operation<
"/codes_of_conduct",
"get",
"scarlet-witch"
>;
}
// test
type GetApp = Endpoints["GET /app"]["parameters"];
export const getApp: GetApp = {};
type GetCodesOfConducts = Endpoints["GET /codes_of_conduct"]["parameters"];
export const getCodesOfConducts: GetCodesOfConducts = {
mediaType: {
previews: ["scarlet-witch"],
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment