-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
'Canvas#getContext' almost never returns 'null' #19229
Comments
From the spec: https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-getcontext
So the definition for |
For reference, let canvas: HTMLCanvasElement = document.createElement("canvas");
let ctx: CanvasRenderingContext2D | null;
if (!(ctx = canvas.getContext("2d"))) {
throw new Error(`2d context not supported or canvas already initialized`);
}
ctx.fillRect(0, 0, canvas.width, canvas.height); Is a decent way to use it in a typesafe way without really getting in the way; but if you must remove |
This just seems like a perfect use case for let canvas: HTMLCanvasElement = document.createElement("canvas");
let ctx: CanvasRenderingContext2D = canvas.getContext("2d")!; // <--
ctx.fillRect(0, 0, canvas.width, canvas.height); |
@NaridaL |
Thank you |
This wrapper handles the possible null value for `getContext()`. Removing the need to check for it inline. See microsoft/TypeScript#19229 for more information.
This wrapper handles the possible null value for `getContext()`. Removing the need to check for it inline. See microsoft/TypeScript#19229 for more information.
- make heavy use of the multiple iterations on types in others' PRs, my own iterations on that, and then @types/react-signature-canvas - credit where credit is due! - refactor init logic a bit to get around `| null` everywhere - kinda hacky but better than ifs or ! (non-nullable) everywhere - use ! for canvas.getContext('2d') calls for same reason - c.f. microsoft/TypeScript#19229 - refactor naming everywhere to src/index.tsx - configure tsconfig -- no need for allowJs anymore, but jsx needs to be set as TS is actually reading it now - configure jest -- ts-jest needed to read TSX files (deps): add DT typings for prop-types, react, and signature_pad (deps): add a declaration file for trim-canvas as it doesn't have types built-in yet (TODO for me!) - use typings/ to hold external declarations
- make heavy use of the multiple iterations on types in others' PRs, my own iterations on that, and then @types/react-signature-canvas - credit where credit is due! - refactor init logic a bit to get around `| null` everywhere - kinda hacky but better than ifs or ! (non-nullable) everywhere - use ! for canvas.getContext('2d') calls for same reason - c.f. microsoft/TypeScript#19229 - refactor naming everywhere to src/index.tsx - configure tsconfig -- no need for allowJs anymore, but jsx needs to be set as TS is actually reading it now - configure jest -- ts-jest needed to read TSX files (deps): add DT typings for prop-types, react, and signature_pad (deps): add a declaration file for trim-canvas as it doesn't have types built-in yet (TODO for me!) - use typings/ to hold external declarations
I know this hasn't had any activity in ages, but I noticed that the relevant MDN page had incorrect information on it, saying that I already put an edit into the MDN page, flagged for technical review (I'm just some guy, you know?) but it sounds to me like this issue can be closed. Basically, the compiler can't know ahead of time if a given call will return |
In theory yes, in reality, it can return
I just got a similar issue in my project :) |
- make heavy use of the multiple iterations on types in others' PRs, my own iterations on that, and then @types/react-signature-canvas - credit where credit is due! - refactor init logic a bit to get around `| null` everywhere - kinda hacky but better than ifs or ! (non-nullable) everywhere - use ! for canvas.getContext('2d') calls for same reason - c.f. microsoft/TypeScript#19229 - refactor naming everywhere to src/index.tsx - configure tsconfig -- no need for allowJs anymore, but jsx needs to be set as TS is actually reading it now - configure jest -- ts-jest needed to read TSX files (deps): add DT typings for prop-types, react, and signature_pad (deps): add a declaration file for trim-canvas as it doesn't have types built-in yet (TODO for me!) - use typings/ to hold external declarations
- make heavy use of the multiple iterations on types in others' PRs, my own iterations on that, and then @types/react-signature-canvas - credit where credit is due! - refactor init logic a bit to get around `| null` everywhere - kinda hacky but better than ifs or ! (non-nullable) everywhere - use ! for canvas.getContext('2d') calls for same reason - c.f. microsoft/TypeScript#19229 - refactor naming everywhere to src/index.tsx - configure tsconfig -- no need for allowJs anymore, but jsx needs to be set as TS is actually reading it now - configure jest -- ts-jest needed to read TSX files (deps): add DT typings for prop-types, react, and signature_pad (deps): add a declaration file for trim-canvas as it doesn't have types built-in yet (TODO for me!) - use typings/ to hold external declarations
- make heavy use of the multiple iterations on types in others' PRs, my own iterations on that, and then @types/react-signature-canvas - credit where credit is due! - NOTE: the naming of the props type and the class type do not include the word "React" unlike @types/react-signature-canvas - it is convention in React components to leave it out and you're already importing from a package with "react" in its name - refactor init logic a bit to get around `| null` everywhere - kinda hacky but better than ifs or ! (non-nullable) everywhere - use ! for canvas.getContext('2d') calls for same reason - c.f. microsoft/TypeScript#19229 - refactor naming everywhere to src/index.tsx - configure tsconfig -- no need for allowJs anymore, but jsx needs to be set as TS is actually reading it now - configure jest -- ts-jest needed to read TSX files (deps): add DT typings for prop-types, react, and signature_pad (deps): add a declaration file for trim-canvas as it doesn't have types built-in yet (TODO for me!) - use typings/ to hold external declarations
- make heavy use of the multiple iterations on types in others' PRs, my own iterations on that, and then @types/react-signature-canvas - credit where credit is due! - NOTE: the naming of the props type and the class type do not include the word "React" unlike @types/react-signature-canvas - it is convention in React components to leave it out and you're already importing from a package with "react" in its name - refactor init logic a bit to get around `| null` everywhere - kinda hacky but better than ifs or ! (non-nullable) everywhere - use ! for canvas.getContext('2d') calls for same reason - c.f. microsoft/TypeScript#19229 - refactor naming everywhere to src/index.tsx - configure tsconfig -- no need for allowJs anymore, but jsx needs to be set as TS is actually reading it now - configure jest -- ts-jest needed to read TSX files (deps): add DT typings for prop-types, react, and signature_pad (deps): add a declaration file for trim-canvas as it doesn't have types built-in yet (TODO for me!) - use typings/ to hold external declarations
I did this and it is enough to suppress the error in vscode: |
That just throws a confusing error if the canvas can not produce a // Use the ! operator to assure TypeScript that
// the value is not null or undefined
let context = canvasElement.getContext('2d')!; |
Why is this still open? The type For example: const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
ctx.fillRect(...); Open your app on mac Safari 14 or 15 and you will see nice errors in your console. Safari sometimes can't create canvas element and immediately return its context, it will return if (!ctx) {
return;
} And that's just only one example (usually its a bit more complex, this was just simple out of context code) I can think on top of my head! And as mentioned in previous replies - there can be even more different cases of weird devices, rendering support etc. |
You misunderstood my comment. The reason the API is typed the way it is and the correct way to handle the situation was already established early in the thread. I'm not suggesting that ignoring the possibility of null is the "correct" solution. I'm suggesting that if you want to ignore the possibility of null, Creating a new rendering context will still fail, but in a more confusing way whereas |
I'm not sure that willfully overriding type safety makes it "easy" to trace the problem. Experienced devs might look at const ctx = myCanvas.getContext("2d")!; // Definitely not null, trust me
ctx.fillRect(...); // why did this explode?? with a skeptical eye, but plenty of people would take that at face value. Safer to do const ctx = myCanvas.getContext("2d");
if (!ctx) { throw new Error("Failed to get context"); } // Should never happen One extra line of code (two if you count adding |
The original question has been asked and answered. I think this horse is dead enough. |
TypeScript Version: 2.5.3
Code:
Expected behavior:
No errors should occur because the type of canvas is HTMLCanvasElement.
Actual behavior:
If I change the type of ctx to
CanvasRenderingContext2D | null
, then I getThe text was updated successfully, but these errors were encountered: