refactor(interfaces): Make type predicates more robust - #9150

Merged
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates
Jun 25, 2025
Merged

refactor(interfaces): Make type predicates more robust#9150
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates

Conversation

@cpcallen

Copy link
Copy Markdown
Collaborator

The basics

The details

Proposed Changes

Make type predicates for interface types more robust:

  • Always use typeof … === 'function' when testing for presence of methods.
  • Test for presence of all non-optional members.
  • Accept input of type any but always check that the value is neither null nor undefined.

Reason for Changes

Because many of these interfaces define developer-supplied types, making the corresponding type predicates more exacting will help ensure that we do not inadvertently accept a value that only partially conforms to the interface.

For example, given some object

constobj={a: 0,b: 'hello',c(){console.log('hello');}d: undefined,// There is no e.}

the typical tests we use will all return true for almost any non-missing value:

obj.a!==undefined// trueobj.b!==undefined// trueobj.c!==undefined// trueobj.d!==undefined// falseobj.e!==undefined// false

while a lazier test might catch a primitive being supplied where a method was expected, at least if the primitive is falsey:

Boolean(obj.a)// FALSEBoolean(obj.b)// trueBoolean(obj.c)// trueBoolean(obj.d)// falseBoolean(obj.e)// false

whereas using typeof will ensure that, at least for primitive- or method-valued members of the interface, the type is approximately as expected:

typeofobj.a==='number'// truetypeofobj.a==='string'// falsetypeofobj.a==='function'// falsetypeofobj.b==='function'// falsetypeofobj.c==='function'// truetypeofobj.d==='function'// falsetypeofobj.e==='function'// false

Notably, the only values whose typeof is 'function' that are not callable as a method are ES6 class constructors (e.g., class C {}), which need to be invoked with new.

Test Coverage

Passes npm test.

Additional Information

One possible argument against this change might be that the type predicates will be slightly more expensive to invoke—though I suspect that in most situations the JIT will optimise away most of the actual checks. Likely only use of these predicates inside hotspot inner loops is likely to be of any measurable effect, but it would be useful if we had some benchmarks so we could actually test to see what effect if any this change might have.

Testing for
'name' in object
or
obj.name !== undefined
only checks for the existence of the property (and in the latter
case that the property is not set to undefined). That's fine if
the interface specifies a property of indeterminate type, but in
the usual case that the interface member is a method we can do
one better and check to make sure the property's value is
callable.
Since most type predicates take an argument of type any but then
check for the existence of certain properties, explicitly check
that the argument is not null or undefined (or check implicitly
by calling another type predicate that does so first, which
necessitates adding a few casts because tsc infers the type of
the argument too narrowly).
@cpcallen
cpcallen requested a review from a team as a code ownerJune 17, 2025 13:49
@github-actionsgithub-actionsBot added the PR: refactor Refactors code label Jun 17, 2025
@cpcallen
cpcallen marked this pull request as draft June 17, 2025 14:51
@cpcallen

Copy link
Copy Markdown
CollaboratorAuthor

Marking this as draft for now because it turns out that some of our own test mocks do not conform to their specified types!

Introduce a new MockFocusable, and add methods to MockIcon,
MockBubbleIcon and MockComment, so that they fulfil the
IFocusableNode, IIcon, IHasBubble and ICommentIcon interfaces
respectively.
Add (test) runtime assertions that:
- isFocusableNode(MockFocusable) returns true
- isIcon(MockIcon) returns true
- hasBubble(MockBubbleIcon) returns true
- isCommentIcon(MockCommentIcon) returns true
(The latter is currently failing because Blockly is undefined when
isCommentIcon calls the MockCommentIcon's getType method.)
For some reason the global Blockly binding is not visible at the
time when isCommentIcon calls MockCommentIcon's getType method,
and presumably this problem would apply to getBubbleSize too,
so directly import the required items.
This slightly simplifies it and makes it less likely to accidentally
stop conforming to IHasBubble.
Fix an error which caused ISelectable instances to fail
isSelectable() checks, one of the results of which is that
Blockly.common.getSelected() would generally return null.
Whoops!
@cpcallen
cpcallen marked this pull request as ready for review June 19, 2025 16:29
@cpcallen
cpcallen requested a review from maribethbJune 19, 2025 16:29
@cpcallen
cpcallen merged commit f4dbea0 into RaspberryPiFoundation:developJun 25, 2025
@cpcallen
cpcallen deleted the fix/interface-predicates branch June 25, 2025 11:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: refactorRefactors code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@cpcallen@maribethb@rachel-fenichel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

refactor(interfaces): Make type predicates more robust - #9150

Merged
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates
Jun 25, 2025
Merged

refactor(interfaces): Make type predicates more robust#9150
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates

Conversation

@cpcallen

Copy link
Copy Markdown
Collaborator

The basics

The details

Proposed Changes

Make type predicates for interface types more robust:

  • Always use typeof … === 'function' when testing for presence of methods.
  • Test for presence of all non-optional members.
  • Accept input of type any but always check that the value is neither null nor undefined.

Reason for Changes

Because many of these interfaces define developer-supplied types, making the corresponding type predicates more exacting will help ensure that we do not inadvertently accept a value that only partially conforms to the interface.

For example, given some object

constobj={a: 0,b: 'hello',c(){console.log('hello');}d: undefined,// There is no e.}

the typical tests we use will all return true for almost any non-missing value:

obj.a!==undefined// trueobj.b!==undefined// trueobj.c!==undefined// trueobj.d!==undefined// falseobj.e!==undefined// false

while a lazier test might catch a primitive being supplied where a method was expected, at least if the primitive is falsey:

Boolean(obj.a)// FALSEBoolean(obj.b)// trueBoolean(obj.c)// trueBoolean(obj.d)// falseBoolean(obj.e)// false

whereas using typeof will ensure that, at least for primitive- or method-valued members of the interface, the type is approximately as expected:

typeofobj.a==='number'// truetypeofobj.a==='string'// falsetypeofobj.a==='function'// falsetypeofobj.b==='function'// falsetypeofobj.c==='function'// truetypeofobj.d==='function'// falsetypeofobj.e==='function'// false

Notably, the only values whose typeof is 'function' that are not callable as a method are ES6 class constructors (e.g., class C {}), which need to be invoked with new.

Test Coverage

Passes npm test.

Additional Information

One possible argument against this change might be that the type predicates will be slightly more expensive to invoke—though I suspect that in most situations the JIT will optimise away most of the actual checks. Likely only use of these predicates inside hotspot inner loops is likely to be of any measurable effect, but it would be useful if we had some benchmarks so we could actually test to see what effect if any this change might have.

Testing for
'name' in object
or
obj.name !== undefined
only checks for the existence of the property (and in the latter
case that the property is not set to undefined). That's fine if
the interface specifies a property of indeterminate type, but in
the usual case that the interface member is a method we can do
one better and check to make sure the property's value is
callable.
Since most type predicates take an argument of type any but then
check for the existence of certain properties, explicitly check
that the argument is not null or undefined (or check implicitly
by calling another type predicate that does so first, which
necessitates adding a few casts because tsc infers the type of
the argument too narrowly).
@cpcallen
cpcallen requested a review from a team as a code ownerJune 17, 2025 13:49
@github-actionsgithub-actionsBot added the PR: refactor Refactors code label Jun 17, 2025
@cpcallen
cpcallen marked this pull request as draft June 17, 2025 14:51
@cpcallen

Copy link
Copy Markdown
CollaboratorAuthor

Marking this as draft for now because it turns out that some of our own test mocks do not conform to their specified types!

Introduce a new MockFocusable, and add methods to MockIcon,
MockBubbleIcon and MockComment, so that they fulfil the
IFocusableNode, IIcon, IHasBubble and ICommentIcon interfaces
respectively.
Add (test) runtime assertions that:
- isFocusableNode(MockFocusable) returns true
- isIcon(MockIcon) returns true
- hasBubble(MockBubbleIcon) returns true
- isCommentIcon(MockCommentIcon) returns true
(The latter is currently failing because Blockly is undefined when
isCommentIcon calls the MockCommentIcon's getType method.)
For some reason the global Blockly binding is not visible at the
time when isCommentIcon calls MockCommentIcon's getType method,
and presumably this problem would apply to getBubbleSize too,
so directly import the required items.
This slightly simplifies it and makes it less likely to accidentally
stop conforming to IHasBubble.
Fix an error which caused ISelectable instances to fail
isSelectable() checks, one of the results of which is that
Blockly.common.getSelected() would generally return null.
Whoops!
@cpcallen
cpcallen marked this pull request as ready for review June 19, 2025 16:29
@cpcallen
cpcallen requested a review from maribethbJune 19, 2025 16:29
@cpcallen
cpcallen merged commit f4dbea0 into RaspberryPiFoundation:developJun 25, 2025
@cpcallen
cpcallen deleted the fix/interface-predicates branch June 25, 2025 11:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: refactorRefactors code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@cpcallen@maribethb@rachel-fenichel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(interfaces): Make type predicates more robust - #9150

Merged
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates
Jun 25, 2025
Merged

refactor(interfaces): Make type predicates more robust#9150
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates

Conversation

@cpcallen

Copy link
Copy Markdown
Collaborator

The basics

The details

Proposed Changes

Make type predicates for interface types more robust:

  • Always use typeof … === 'function' when testing for presence of methods.
  • Test for presence of all non-optional members.
  • Accept input of type any but always check that the value is neither null nor undefined.

Reason for Changes

Because many of these interfaces define developer-supplied types, making the corresponding type predicates more exacting will help ensure that we do not inadvertently accept a value that only partially conforms to the interface.

For example, given some object

constobj={a: 0,b: 'hello',c(){console.log('hello');}d: undefined,// There is no e.}

the typical tests we use will all return true for almost any non-missing value:

obj.a!==undefined// trueobj.b!==undefined// trueobj.c!==undefined// trueobj.d!==undefined// falseobj.e!==undefined// false

while a lazier test might catch a primitive being supplied where a method was expected, at least if the primitive is falsey:

Boolean(obj.a)// FALSEBoolean(obj.b)// trueBoolean(obj.c)// trueBoolean(obj.d)// falseBoolean(obj.e)// false

whereas using typeof will ensure that, at least for primitive- or method-valued members of the interface, the type is approximately as expected:

typeofobj.a==='number'// truetypeofobj.a==='string'// falsetypeofobj.a==='function'// falsetypeofobj.b==='function'// falsetypeofobj.c==='function'// truetypeofobj.d==='function'// falsetypeofobj.e==='function'// false

Notably, the only values whose typeof is 'function' that are not callable as a method are ES6 class constructors (e.g., class C {}), which need to be invoked with new.

Test Coverage

Passes npm test.

Additional Information

One possible argument against this change might be that the type predicates will be slightly more expensive to invoke—though I suspect that in most situations the JIT will optimise away most of the actual checks. Likely only use of these predicates inside hotspot inner loops is likely to be of any measurable effect, but it would be useful if we had some benchmarks so we could actually test to see what effect if any this change might have.

Testing for
'name' in object
or
obj.name !== undefined
only checks for the existence of the property (and in the latter
case that the property is not set to undefined). That's fine if
the interface specifies a property of indeterminate type, but in
the usual case that the interface member is a method we can do
one better and check to make sure the property's value is
callable.
Since most type predicates take an argument of type any but then
check for the existence of certain properties, explicitly check
that the argument is not null or undefined (or check implicitly
by calling another type predicate that does so first, which
necessitates adding a few casts because tsc infers the type of
the argument too narrowly).
@cpcallen
cpcallen requested a review from a team as a code ownerJune 17, 2025 13:49
@github-actionsgithub-actionsBot added the PR: refactor Refactors code label Jun 17, 2025
@cpcallen
cpcallen marked this pull request as draft June 17, 2025 14:51
@cpcallen

Copy link
Copy Markdown
CollaboratorAuthor

Marking this as draft for now because it turns out that some of our own test mocks do not conform to their specified types!

Introduce a new MockFocusable, and add methods to MockIcon,
MockBubbleIcon and MockComment, so that they fulfil the
IFocusableNode, IIcon, IHasBubble and ICommentIcon interfaces
respectively.
Add (test) runtime assertions that:
- isFocusableNode(MockFocusable) returns true
- isIcon(MockIcon) returns true
- hasBubble(MockBubbleIcon) returns true
- isCommentIcon(MockCommentIcon) returns true
(The latter is currently failing because Blockly is undefined when
isCommentIcon calls the MockCommentIcon's getType method.)
For some reason the global Blockly binding is not visible at the
time when isCommentIcon calls MockCommentIcon's getType method,
and presumably this problem would apply to getBubbleSize too,
so directly import the required items.
This slightly simplifies it and makes it less likely to accidentally
stop conforming to IHasBubble.
Fix an error which caused ISelectable instances to fail
isSelectable() checks, one of the results of which is that
Blockly.common.getSelected() would generally return null.
Whoops!
@cpcallen
cpcallen marked this pull request as ready for review June 19, 2025 16:29
@cpcallen
cpcallen requested a review from maribethbJune 19, 2025 16:29
@cpcallen
cpcallen merged commit f4dbea0 into RaspberryPiFoundation:developJun 25, 2025
@cpcallen
cpcallen deleted the fix/interface-predicates branch June 25, 2025 11:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: refactorRefactors code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@cpcallen@maribethb@rachel-fenichel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(interfaces): Make type predicates more robust - #9150

Merged
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates
Jun 25, 2025
Merged

refactor(interfaces): Make type predicates more robust#9150
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates

Conversation

@cpcallen

Copy link
Copy Markdown
Collaborator

The basics

The details

Proposed Changes

Make type predicates for interface types more robust:

  • Always use typeof … === 'function' when testing for presence of methods.
  • Test for presence of all non-optional members.
  • Accept input of type any but always check that the value is neither null nor undefined.

Reason for Changes

Because many of these interfaces define developer-supplied types, making the corresponding type predicates more exacting will help ensure that we do not inadvertently accept a value that only partially conforms to the interface.

For example, given some object

constobj={a: 0,b: 'hello',c(){console.log('hello');}d: undefined,// There is no e.}

the typical tests we use will all return true for almost any non-missing value:

obj.a!==undefined// trueobj.b!==undefined// trueobj.c!==undefined// trueobj.d!==undefined// falseobj.e!==undefined// false

while a lazier test might catch a primitive being supplied where a method was expected, at least if the primitive is falsey:

Boolean(obj.a)// FALSEBoolean(obj.b)// trueBoolean(obj.c)// trueBoolean(obj.d)// falseBoolean(obj.e)// false

whereas using typeof will ensure that, at least for primitive- or method-valued members of the interface, the type is approximately as expected:

typeofobj.a==='number'// truetypeofobj.a==='string'// falsetypeofobj.a==='function'// falsetypeofobj.b==='function'// falsetypeofobj.c==='function'// truetypeofobj.d==='function'// falsetypeofobj.e==='function'// false

Notably, the only values whose typeof is 'function' that are not callable as a method are ES6 class constructors (e.g., class C {}), which need to be invoked with new.

Test Coverage

Passes npm test.

Additional Information

One possible argument against this change might be that the type predicates will be slightly more expensive to invoke—though I suspect that in most situations the JIT will optimise away most of the actual checks. Likely only use of these predicates inside hotspot inner loops is likely to be of any measurable effect, but it would be useful if we had some benchmarks so we could actually test to see what effect if any this change might have.

Testing for
'name' in object
or
obj.name !== undefined
only checks for the existence of the property (and in the latter
case that the property is not set to undefined). That's fine if
the interface specifies a property of indeterminate type, but in
the usual case that the interface member is a method we can do
one better and check to make sure the property's value is
callable.
Since most type predicates take an argument of type any but then
check for the existence of certain properties, explicitly check
that the argument is not null or undefined (or check implicitly
by calling another type predicate that does so first, which
necessitates adding a few casts because tsc infers the type of
the argument too narrowly).
@cpcallen
cpcallen requested a review from a team as a code ownerJune 17, 2025 13:49
@github-actionsgithub-actionsBot added the PR: refactor Refactors code label Jun 17, 2025
@cpcallen
cpcallen marked this pull request as draft June 17, 2025 14:51
@cpcallen

Copy link
Copy Markdown
CollaboratorAuthor

Marking this as draft for now because it turns out that some of our own test mocks do not conform to their specified types!

Introduce a new MockFocusable, and add methods to MockIcon,
MockBubbleIcon and MockComment, so that they fulfil the
IFocusableNode, IIcon, IHasBubble and ICommentIcon interfaces
respectively.
Add (test) runtime assertions that:
- isFocusableNode(MockFocusable) returns true
- isIcon(MockIcon) returns true
- hasBubble(MockBubbleIcon) returns true
- isCommentIcon(MockCommentIcon) returns true
(The latter is currently failing because Blockly is undefined when
isCommentIcon calls the MockCommentIcon's getType method.)
For some reason the global Blockly binding is not visible at the
time when isCommentIcon calls MockCommentIcon's getType method,
and presumably this problem would apply to getBubbleSize too,
so directly import the required items.
This slightly simplifies it and makes it less likely to accidentally
stop conforming to IHasBubble.
Fix an error which caused ISelectable instances to fail
isSelectable() checks, one of the results of which is that
Blockly.common.getSelected() would generally return null.
Whoops!
@cpcallen
cpcallen marked this pull request as ready for review June 19, 2025 16:29
@cpcallen
cpcallen requested a review from maribethbJune 19, 2025 16:29
@cpcallen
cpcallen merged commit f4dbea0 into RaspberryPiFoundation:developJun 25, 2025
@cpcallen
cpcallen deleted the fix/interface-predicates branch June 25, 2025 11:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: refactorRefactors code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@cpcallen@maribethb@rachel-fenichel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

refactor(interfaces): Make type predicates more robust - #9150

Merged
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates
Jun 25, 2025
Merged

refactor(interfaces): Make type predicates more robust#9150
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates

Conversation

@cpcallen

Copy link
Copy Markdown
Collaborator

The basics

The details

Proposed Changes

Make type predicates for interface types more robust:

  • Always use typeof … === 'function' when testing for presence of methods.
  • Test for presence of all non-optional members.
  • Accept input of type any but always check that the value is neither null nor undefined.

Reason for Changes

Because many of these interfaces define developer-supplied types, making the corresponding type predicates more exacting will help ensure that we do not inadvertently accept a value that only partially conforms to the interface.

For example, given some object

constobj={a: 0,b: 'hello',c(){console.log('hello');}d: undefined,// There is no e.}

the typical tests we use will all return true for almost any non-missing value:

obj.a!==undefined// trueobj.b!==undefined// trueobj.c!==undefined// trueobj.d!==undefined// falseobj.e!==undefined// false

while a lazier test might catch a primitive being supplied where a method was expected, at least if the primitive is falsey:

Boolean(obj.a)// FALSEBoolean(obj.b)// trueBoolean(obj.c)// trueBoolean(obj.d)// falseBoolean(obj.e)// false

whereas using typeof will ensure that, at least for primitive- or method-valued members of the interface, the type is approximately as expected:

typeofobj.a==='number'// truetypeofobj.a==='string'// falsetypeofobj.a==='function'// falsetypeofobj.b==='function'// falsetypeofobj.c==='function'// truetypeofobj.d==='function'// falsetypeofobj.e==='function'// false

Notably, the only values whose typeof is 'function' that are not callable as a method are ES6 class constructors (e.g., class C {}), which need to be invoked with new.

Test Coverage

Passes npm test.

Additional Information

One possible argument against this change might be that the type predicates will be slightly more expensive to invoke—though I suspect that in most situations the JIT will optimise away most of the actual checks. Likely only use of these predicates inside hotspot inner loops is likely to be of any measurable effect, but it would be useful if we had some benchmarks so we could actually test to see what effect if any this change might have.

Testing for
'name' in object
or
obj.name !== undefined
only checks for the existence of the property (and in the latter
case that the property is not set to undefined). That's fine if
the interface specifies a property of indeterminate type, but in
the usual case that the interface member is a method we can do
one better and check to make sure the property's value is
callable.
Since most type predicates take an argument of type any but then
check for the existence of certain properties, explicitly check
that the argument is not null or undefined (or check implicitly
by calling another type predicate that does so first, which
necessitates adding a few casts because tsc infers the type of
the argument too narrowly).
@cpcallen
cpcallen requested a review from a team as a code ownerJune 17, 2025 13:49
@github-actionsgithub-actionsBot added the PR: refactor Refactors code label Jun 17, 2025
@cpcallen
cpcallen marked this pull request as draft June 17, 2025 14:51
@cpcallen

Copy link
Copy Markdown
CollaboratorAuthor

Marking this as draft for now because it turns out that some of our own test mocks do not conform to their specified types!

Introduce a new MockFocusable, and add methods to MockIcon,
MockBubbleIcon and MockComment, so that they fulfil the
IFocusableNode, IIcon, IHasBubble and ICommentIcon interfaces
respectively.
Add (test) runtime assertions that:
- isFocusableNode(MockFocusable) returns true
- isIcon(MockIcon) returns true
- hasBubble(MockBubbleIcon) returns true
- isCommentIcon(MockCommentIcon) returns true
(The latter is currently failing because Blockly is undefined when
isCommentIcon calls the MockCommentIcon's getType method.)
For some reason the global Blockly binding is not visible at the
time when isCommentIcon calls MockCommentIcon's getType method,
and presumably this problem would apply to getBubbleSize too,
so directly import the required items.
This slightly simplifies it and makes it less likely to accidentally
stop conforming to IHasBubble.
Fix an error which caused ISelectable instances to fail
isSelectable() checks, one of the results of which is that
Blockly.common.getSelected() would generally return null.
Whoops!
@cpcallen
cpcallen marked this pull request as ready for review June 19, 2025 16:29
@cpcallen
cpcallen requested a review from maribethbJune 19, 2025 16:29
@cpcallen
cpcallen merged commit f4dbea0 into RaspberryPiFoundation:developJun 25, 2025
@cpcallen
cpcallen deleted the fix/interface-predicates branch June 25, 2025 11:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: refactorRefactors code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@cpcallen@maribethb@rachel-fenichel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(interfaces): Make type predicates more robust - #9150

Merged
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates
Jun 25, 2025
Merged

refactor(interfaces): Make type predicates more robust#9150
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates

Conversation

@cpcallen

Copy link
Copy Markdown
Collaborator

The basics

The details

Proposed Changes

Make type predicates for interface types more robust:

  • Always use typeof … === 'function' when testing for presence of methods.
  • Test for presence of all non-optional members.
  • Accept input of type any but always check that the value is neither null nor undefined.

Reason for Changes

Because many of these interfaces define developer-supplied types, making the corresponding type predicates more exacting will help ensure that we do not inadvertently accept a value that only partially conforms to the interface.

For example, given some object

constobj={a: 0,b: 'hello',c(){console.log('hello');}d: undefined,// There is no e.}

the typical tests we use will all return true for almost any non-missing value:

obj.a!==undefined// trueobj.b!==undefined// trueobj.c!==undefined// trueobj.d!==undefined// falseobj.e!==undefined// false

while a lazier test might catch a primitive being supplied where a method was expected, at least if the primitive is falsey:

Boolean(obj.a)// FALSEBoolean(obj.b)// trueBoolean(obj.c)// trueBoolean(obj.d)// falseBoolean(obj.e)// false

whereas using typeof will ensure that, at least for primitive- or method-valued members of the interface, the type is approximately as expected:

typeofobj.a==='number'// truetypeofobj.a==='string'// falsetypeofobj.a==='function'// falsetypeofobj.b==='function'// falsetypeofobj.c==='function'// truetypeofobj.d==='function'// falsetypeofobj.e==='function'// false

Notably, the only values whose typeof is 'function' that are not callable as a method are ES6 class constructors (e.g., class C {}), which need to be invoked with new.

Test Coverage

Passes npm test.

Additional Information

One possible argument against this change might be that the type predicates will be slightly more expensive to invoke—though I suspect that in most situations the JIT will optimise away most of the actual checks. Likely only use of these predicates inside hotspot inner loops is likely to be of any measurable effect, but it would be useful if we had some benchmarks so we could actually test to see what effect if any this change might have.

Testing for
'name' in object
or
obj.name !== undefined
only checks for the existence of the property (and in the latter
case that the property is not set to undefined). That's fine if
the interface specifies a property of indeterminate type, but in
the usual case that the interface member is a method we can do
one better and check to make sure the property's value is
callable.
Since most type predicates take an argument of type any but then
check for the existence of certain properties, explicitly check
that the argument is not null or undefined (or check implicitly
by calling another type predicate that does so first, which
necessitates adding a few casts because tsc infers the type of
the argument too narrowly).
@cpcallen
cpcallen requested a review from a team as a code ownerJune 17, 2025 13:49
@github-actionsgithub-actionsBot added the PR: refactor Refactors code label Jun 17, 2025
@cpcallen
cpcallen marked this pull request as draft June 17, 2025 14:51
@cpcallen

Copy link
Copy Markdown
CollaboratorAuthor

Marking this as draft for now because it turns out that some of our own test mocks do not conform to their specified types!

Introduce a new MockFocusable, and add methods to MockIcon,
MockBubbleIcon and MockComment, so that they fulfil the
IFocusableNode, IIcon, IHasBubble and ICommentIcon interfaces
respectively.
Add (test) runtime assertions that:
- isFocusableNode(MockFocusable) returns true
- isIcon(MockIcon) returns true
- hasBubble(MockBubbleIcon) returns true
- isCommentIcon(MockCommentIcon) returns true
(The latter is currently failing because Blockly is undefined when
isCommentIcon calls the MockCommentIcon's getType method.)
For some reason the global Blockly binding is not visible at the
time when isCommentIcon calls MockCommentIcon's getType method,
and presumably this problem would apply to getBubbleSize too,
so directly import the required items.
This slightly simplifies it and makes it less likely to accidentally
stop conforming to IHasBubble.
Fix an error which caused ISelectable instances to fail
isSelectable() checks, one of the results of which is that
Blockly.common.getSelected() would generally return null.
Whoops!
@cpcallen
cpcallen marked this pull request as ready for review June 19, 2025 16:29
@cpcallen
cpcallen requested a review from maribethbJune 19, 2025 16:29
@cpcallen
cpcallen merged commit f4dbea0 into RaspberryPiFoundation:developJun 25, 2025
@cpcallen
cpcallen deleted the fix/interface-predicates branch June 25, 2025 11:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: refactorRefactors code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@cpcallen@maribethb@rachel-fenichel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(interfaces): Make type predicates more robust - #9150

Merged
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates
Jun 25, 2025
Merged

refactor(interfaces): Make type predicates more robust#9150
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates

Conversation

@cpcallen

Copy link
Copy Markdown
Collaborator

The basics

The details

Proposed Changes

Make type predicates for interface types more robust:

  • Always use typeof … === 'function' when testing for presence of methods.
  • Test for presence of all non-optional members.
  • Accept input of type any but always check that the value is neither null nor undefined.

Reason for Changes

Because many of these interfaces define developer-supplied types, making the corresponding type predicates more exacting will help ensure that we do not inadvertently accept a value that only partially conforms to the interface.

For example, given some object

constobj={a: 0,b: 'hello',c(){console.log('hello');}d: undefined,// There is no e.}

the typical tests we use will all return true for almost any non-missing value:

obj.a!==undefined// trueobj.b!==undefined// trueobj.c!==undefined// trueobj.d!==undefined// falseobj.e!==undefined// false

while a lazier test might catch a primitive being supplied where a method was expected, at least if the primitive is falsey:

Boolean(obj.a)// FALSEBoolean(obj.b)// trueBoolean(obj.c)// trueBoolean(obj.d)// falseBoolean(obj.e)// false

whereas using typeof will ensure that, at least for primitive- or method-valued members of the interface, the type is approximately as expected:

typeofobj.a==='number'// truetypeofobj.a==='string'// falsetypeofobj.a==='function'// falsetypeofobj.b==='function'// falsetypeofobj.c==='function'// truetypeofobj.d==='function'// falsetypeofobj.e==='function'// false

Notably, the only values whose typeof is 'function' that are not callable as a method are ES6 class constructors (e.g., class C {}), which need to be invoked with new.

Test Coverage

Passes npm test.

Additional Information

One possible argument against this change might be that the type predicates will be slightly more expensive to invoke—though I suspect that in most situations the JIT will optimise away most of the actual checks. Likely only use of these predicates inside hotspot inner loops is likely to be of any measurable effect, but it would be useful if we had some benchmarks so we could actually test to see what effect if any this change might have.

Testing for
'name' in object
or
obj.name !== undefined
only checks for the existence of the property (and in the latter
case that the property is not set to undefined). That's fine if
the interface specifies a property of indeterminate type, but in
the usual case that the interface member is a method we can do
one better and check to make sure the property's value is
callable.
Since most type predicates take an argument of type any but then
check for the existence of certain properties, explicitly check
that the argument is not null or undefined (or check implicitly
by calling another type predicate that does so first, which
necessitates adding a few casts because tsc infers the type of
the argument too narrowly).
@cpcallen
cpcallen requested a review from a team as a code ownerJune 17, 2025 13:49
@github-actionsgithub-actionsBot added the PR: refactor Refactors code label Jun 17, 2025
@cpcallen
cpcallen marked this pull request as draft June 17, 2025 14:51
@cpcallen

Copy link
Copy Markdown
CollaboratorAuthor

Marking this as draft for now because it turns out that some of our own test mocks do not conform to their specified types!

Introduce a new MockFocusable, and add methods to MockIcon,
MockBubbleIcon and MockComment, so that they fulfil the
IFocusableNode, IIcon, IHasBubble and ICommentIcon interfaces
respectively.
Add (test) runtime assertions that:
- isFocusableNode(MockFocusable) returns true
- isIcon(MockIcon) returns true
- hasBubble(MockBubbleIcon) returns true
- isCommentIcon(MockCommentIcon) returns true
(The latter is currently failing because Blockly is undefined when
isCommentIcon calls the MockCommentIcon's getType method.)
For some reason the global Blockly binding is not visible at the
time when isCommentIcon calls MockCommentIcon's getType method,
and presumably this problem would apply to getBubbleSize too,
so directly import the required items.
This slightly simplifies it and makes it less likely to accidentally
stop conforming to IHasBubble.
Fix an error which caused ISelectable instances to fail
isSelectable() checks, one of the results of which is that
Blockly.common.getSelected() would generally return null.
Whoops!
@cpcallen
cpcallen marked this pull request as ready for review June 19, 2025 16:29
@cpcallen
cpcallen requested a review from maribethbJune 19, 2025 16:29
@cpcallen
cpcallen merged commit f4dbea0 into RaspberryPiFoundation:developJun 25, 2025
@cpcallen
cpcallen deleted the fix/interface-predicates branch June 25, 2025 11:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: refactorRefactors code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@cpcallen@maribethb@rachel-fenichel
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

refactor(interfaces): Make type predicates more robust - #9150

Merged
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates
Jun 25, 2025
Merged

refactor(interfaces): Make type predicates more robust#9150
cpcallen merged 10 commits into
RaspberryPiFoundation:developfrom
cpcallen:fix/interface-predicates

Conversation

@cpcallen

Copy link
Copy Markdown
Collaborator

The basics

The details

Proposed Changes

Make type predicates for interface types more robust:

  • Always use typeof … === 'function' when testing for presence of methods.
  • Test for presence of all non-optional members.
  • Accept input of type any but always check that the value is neither null nor undefined.

Reason for Changes

Because many of these interfaces define developer-supplied types, making the corresponding type predicates more exacting will help ensure that we do not inadvertently accept a value that only partially conforms to the interface.

For example, given some object

constobj={a: 0,b: 'hello',c(){console.log('hello');}d: undefined,// There is no e.}

the typical tests we use will all return true for almost any non-missing value:

obj.a!==undefined// trueobj.b!==undefined// trueobj.c!==undefined// trueobj.d!==undefined// falseobj.e!==undefined// false

while a lazier test might catch a primitive being supplied where a method was expected, at least if the primitive is falsey:

Boolean(obj.a)// FALSEBoolean(obj.b)// trueBoolean(obj.c)// trueBoolean(obj.d)// falseBoolean(obj.e)// false

whereas using typeof will ensure that, at least for primitive- or method-valued members of the interface, the type is approximately as expected:

typeofobj.a==='number'// truetypeofobj.a==='string'// falsetypeofobj.a==='function'// falsetypeofobj.b==='function'// falsetypeofobj.c==='function'// truetypeofobj.d==='function'// falsetypeofobj.e==='function'// false

Notably, the only values whose typeof is 'function' that are not callable as a method are ES6 class constructors (e.g., class C {}), which need to be invoked with new.

Test Coverage

Passes npm test.

Additional Information

One possible argument against this change might be that the type predicates will be slightly more expensive to invoke—though I suspect that in most situations the JIT will optimise away most of the actual checks. Likely only use of these predicates inside hotspot inner loops is likely to be of any measurable effect, but it would be useful if we had some benchmarks so we could actually test to see what effect if any this change might have.

Testing for
'name' in object
or
obj.name !== undefined
only checks for the existence of the property (and in the latter
case that the property is not set to undefined). That's fine if
the interface specifies a property of indeterminate type, but in
the usual case that the interface member is a method we can do
one better and check to make sure the property's value is
callable.
Since most type predicates take an argument of type any but then
check for the existence of certain properties, explicitly check
that the argument is not null or undefined (or check implicitly
by calling another type predicate that does so first, which
necessitates adding a few casts because tsc infers the type of
the argument too narrowly).
@cpcallen
cpcallen requested a review from a team as a code ownerJune 17, 2025 13:49
@github-actionsgithub-actionsBot added the PR: refactor Refactors code label Jun 17, 2025
@cpcallen
cpcallen marked this pull request as draft June 17, 2025 14:51
@cpcallen

Copy link
Copy Markdown
CollaboratorAuthor

Marking this as draft for now because it turns out that some of our own test mocks do not conform to their specified types!

Introduce a new MockFocusable, and add methods to MockIcon,
MockBubbleIcon and MockComment, so that they fulfil the
IFocusableNode, IIcon, IHasBubble and ICommentIcon interfaces
respectively.
Add (test) runtime assertions that:
- isFocusableNode(MockFocusable) returns true
- isIcon(MockIcon) returns true
- hasBubble(MockBubbleIcon) returns true
- isCommentIcon(MockCommentIcon) returns true
(The latter is currently failing because Blockly is undefined when
isCommentIcon calls the MockCommentIcon's getType method.)
For some reason the global Blockly binding is not visible at the
time when isCommentIcon calls MockCommentIcon's getType method,
and presumably this problem would apply to getBubbleSize too,
so directly import the required items.
This slightly simplifies it and makes it less likely to accidentally
stop conforming to IHasBubble.
Fix an error which caused ISelectable instances to fail
isSelectable() checks, one of the results of which is that
Blockly.common.getSelected() would generally return null.
Whoops!
@cpcallen
cpcallen marked this pull request as ready for review June 19, 2025 16:29
@cpcallen
cpcallen requested a review from maribethbJune 19, 2025 16:29
@cpcallen
cpcallen merged commit f4dbea0 into RaspberryPiFoundation:developJun 25, 2025
@cpcallen
cpcallen deleted the fix/interface-predicates branch June 25, 2025 11:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: refactorRefactors code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@cpcallen@maribethb@rachel-fenichel