今日已更新 143 条资讯 | 累计 40588 条内容
关于我们

标签:#TypeScript

找到 727 篇相关文章

AI 资讯

Devlog: capturing smooth game footage from a renderer that never hits 30fps

Hey guys 👋 Quick devlog on the side project. I'm building an open-world stickman superhero game. Flat white surfaces, black outlines, no textures and no colour anywhere. The whole city is built from modules on a grid rather than baked meshes, which is the load-bearing decision of the project: destroying a wall is removing a module and building one is adding it back, so destruction and construction are the same system. This week went into the landscapes, so I wanted a 20 second clip flying through a few of the districts. The bit that was actually interesting I wanted the footage captured out of the real game rather than reconstructed in an editor. The obvious approach is to drive it with Playwright and take a screenshot every frame, but that falls apart immediately: rendering under automation is far slower than a screenshot loop can keep up with, so wall-clock capture stutters and the timing drifts. The fix is to stop letting the clock decide. Before the game boots, hijack requestAnimationFrame and queue the callbacks instead of running them: replace requestAnimationFrame with a function that pushes the callback onto a queue- expose a step(dt) that advances a virtual timestamp and drains the queue- call step(1000 / 30) once per screenshotEvery captured frame now advances the simulation by exactly 1/30th of a second, whatever the renderer is actually doing. A frame that takes 300ms to draw and a frame that takes 8ms produce identical motion. The result is smooth 30fps footage from a renderer that never once hit 30fps, and it is deterministic — the same seed gives you the same clip every time. The same rig drives the camera: for the aerials it detaches the chase camera and dollies an external one between two framings, and for the traversal and combat shots it just feeds synthetic input to the real player controller. Nothing in the video is staged. ## Stack Three.js driven imperatively, Rapier for physics, React for the HUD only, TypeScript in strict mode, packaged with

2026-09-07 原文 →
AI 资讯

The descriptor survived, const did not — full-stack Rust

One skeleton, many screens argued that admin screens should be declared as typed data rather than coded, and it ended by claiming the idea was independent of the stack: draw the boundary as a one-way dependency — domains depend inward on a framework that knows nothing about them — and validate it with a zero-diff refactor of a screen you already trust. That was React and TypeScript. This is the same claim re-run in Rust, where a descriptor can be a compile-time constant and a template is a macro. Because the first result is already published, the second stack is a replication with a control rather than a fresh opinion — which is rare enough to be worth doing properly. Companion to Topcoat and the shrinking cost of full-stack Rust . That post was written from the announcement and promised a follow-up reporting where the rough edges actually show. This is it, from the pilot that followed: a small admin panel built on Topcoat 0.6.2 and Toasty 0.10.0, and the four questions that post committed to answering. The pilot is open source — a clean clone runs both screens and the test that decides the argument. That phrase, a compile-time constant , is where the title comes from, so it is worth saying now what it buys and why I wanted it. A TypeScript descriptor is an array of objects assembled when the module loads. A Rust one can be more than that: &'static , Copy , allocated never, fully checked before the program starts. Going in, that looked to me like the same idea in a stricter form — if declaring a screen as data is good, then declaring it as data the compiler can see through and verify must be better still. I treated that property as the thing worth protecting, and the pilot was partly a test of whether it could be. The stack is deliberately a young one. Topcoat is six weeks old: Tokio's team announced it on 22 July 2026, the pilot pins 0.6.2, and the project still expects breaking changes. It is not the only full-stack Rust framework — Leptos and Dioxus have been at

2026-09-07 原文 →
AI 资讯

vlt 1.0 Ships as a Drop-in npm Replacement with Phased Installs, Graph Queries, and Malware-Blocking

vlt, created by the original npm team, has launched version 1.0 as a drop-in replacement for npm. It features phased installations to prevent automatic script execution, a queryable dependency graph with over 60 selectors, and hosted registries that block malicious packages. The tool aims to enhance security and streamline the JavaScript development process. By Daniel Curtis

2026-09-07 原文 →
AI 资讯

Why I Prefer TypeScript Over JavaScript for Larger Projects

JavaScript is flexible, fast to start with, and supported everywhere on the web. For small scripts, quick experiments, and simple browser utilities, plain JavaScript is often enough. But as projects become larger, TypeScript starts to solve problems that JavaScript leaves entirely up to the developer. That is why I increasingly prefer TypeScript for anything beyond a very small project. The biggest difference is type safety JavaScript lets variables change type freely. For example: let khg5293UserId = 5293; khg5293UserId = "5293"; That is valid JavaScript. Sometimes this flexibility is convenient, but it also makes it easier for unexpected values to move through an application. TypeScript lets you define what a value is supposed to be: let khg5293UserId: number = 5293; Now assigning a string to khg5293UserId produces an error during development. That means certain mistakes are caught before the code ever runs. For small khg5293 experiments, this may not matter much. For a larger application with many files and components, it becomes much more valuable. Functions become easier to understand Consider a JavaScript function: function getProjectName(project) { return project.name; } There is nothing here telling us what project is supposed to contain. With TypeScript, the expected structure can be defined directly: type Khg5293Project = { name: string; language: string; public: boolean; }; function getProjectName(project: Khg5293Project): string { return project.name; } Now the function documents itself. A developer immediately knows what kind of object should be passed into it and what the function returns. This becomes especially useful when returning to a project after several weeks or working across a larger codebase. Interfaces make data structures clearer TypeScript also makes application data easier to reason about. For example: interface Khg5293Profile { username: string; projectCount: number; active: boolean; } const khg5293Profile: Khg5293Profile = { username:

2026-09-07 原文 →
AI 资讯

From Contract Boundary to Error Boundary: Structuring API Error Handling in a TypeScript Frontend

In a previous post , I covered why TypeScript types alone can't protect you from a backend that returns something you didn't expect, and how to build a small apiRequest boundary that validates both the outgoing request and the incoming response against Zod-style schemas before your application ever touches the data. That post answered one question: Is this data actually shaped the way I think it is? It left another question open: When the answer is no, or when the request fails for a completely different reason (like a timeout or a dropped connection), what does the rest of the app do with that failure? In practice, "the rest of the app" usually does something different depending on who's writing it: One component checks error.response?.status directly. Another checks error.code === "ECONNABORTED" . A form manually digs through the error to find field-level messages. A toast just displays whatever string happens to be on error.message . The app works, but every layer speaks a different error dialect. This post is Part 2: it takes the validation boundary from Part 1 and builds the missing piece on top of it, a single, normalized ApiError shape that every layer of the app can speak, plus the logging, messaging, and form-mapping that make it actually usable. Quick Recap: The Validation Boundary From Part 1, the apiRequest wrapper validates request payloads and response bodies against schemas, and throws one of two typed errors when something doesn't match the contract: export class ApiRequestValidationError extends Error { constructor ( public readonly url : string , public override readonly cause : unknown ) { super ( `API request input does not match the contract for ${ url } .` ); this . name = " ApiRequestValidationError " ; } } export class ApiResponseValidationError extends Error { constructor ( public readonly url : string , public override readonly cause : unknown ) { super ( `API response does not match the contract for ${ url } .` ); this . name = " ApiRespon

2026-09-06 原文 →
AI 资讯

Implementing AI Streaming Responses with JSON Lines Chunked Communication Instead of SSE

Background When streaming AI chat responses, Server-Sent Events (SSE) are commonly used. They are also adopted by APIs from OpenAI and Anthropic, as well as by MCP server responses. In fact, I implemented several AI chat projects that modified responses from AI platforms while streaming them to the browser. In doing so, I encountered an issue where SSE did not work because of certain intermediary proxies and load balancers, such as AWS App Runner. After taking a closer look at the SSE specification, I no longer felt that using SSE was right when the purpose was not actually event notification. The API's block data itself is JSON. This is also the same format as the structured logging sent to services such as CloudWatch Logs today (I had already been working on structuring application logs as JSON). Moving from SSE to JSON Lines Chunked Communication What I came up with was a combination of Transfer-Encoding: chunked and Content-Type: application/jsonl (which is not defined by the IAEA). With this approach, even if a proxy or load balancer buffers the response and returns it as a single body rather than chunks, only streaming is lost; the final complete data remains unchanged. Because it is JSON Lines (NDJSON), all you need to do is split on line feeds (LF) and JSON-parse each line. It is also easy to inspect in browser developer tools. However, implementing this from scratch every time is a bit of work, so I implemented and published jsonl-webstream , an npm library of stream utilities for browsers and servers (Node.js). The library has zero dependencies . tilfin / jsonl-webstream Lightweight library for JSON Lines web stream between browsers and Node.js environments jsonl-webstream Lightweight library for JSON Lines web stream between browsers and Node.js environments Overview This library provides utilities for processing JSON Lines formatted data through the Web Streams API It enables efficient streaming of JSON Lines data with minimal memory overhead across brow

2026-09-06 原文 →
AI 资讯

Multi-Agent Does Not Mean Parallel: Safe Workflows with Google ADK

“Let’s split it into agents” has become the AI equivalent of “let’s make it a microservice.” Sometimes the boundary is useful. Sometimes it only creates more state, more coordination, and a harder failure to explain. The most dangerous assumption is that separate agents should run in parallel. Parallelism is safe only when the branches are genuinely independent. If one branch changes the world while another is evaluating it, both agents can make locally reasonable decisions that are unsafe together. Google ADK 2.0 makes workflow topology explicit through graph-based Workflow objects. That is valuable because sequences, branches, and joins become part of the program instead of an agreement hidden in a supervisor prompt. Series note: This is Part 5 of Reliable Google AI Agents in TypeScript . The examples were checked against @google/adk 2.0.0 in September 2026. Start with the dependency, not the agent count Imagine a system preparing a hotel recommendation. It needs live inventory, company travel policy, and a final recommendation. Inventory lookup and policy evaluation can run concurrently because both observe the same request and neither changes shared state. The final decision must wait for both. Now consider a different pair of operations: one agent changes the reservation; another calculates an upgrade using the current reservation. Those branches are not independent. Running them concurrently can make the upgrade decision depend on state that no longer exists. Before drawing a parallel branch, ask: Do both operations only read the same starting state? Can either operation change data the other consumes? Can either produce an irreversible side effect? Is there a deterministic way to combine their results? What happens when one succeeds and the other times out? If those answers are unclear, parallel is an optimization you have not earned yet. Encode safe parallelism as fan-out and join ADK’s TypeScript Workflow graph can express two independent branches and a joi

2026-09-05 原文 →
AI 资讯

The message that mentioned finance and tagged nobody

Somebody types "can we loop in finance on this?" in a product channel. Nobody tags the finance channel. The thread moves on. Three weeks later there is a contract nobody in finance has seen. That is not a tooling problem in any obvious sense. Slack worked exactly as designed. Search would have found the message if anyone had known to look for it. The failure is that the people who needed to know were never told, and nothing in the workspace was watching for the difference between mentioning a team and involving one. Why keyword matching does not solve this The instinct is to grep for the word "finance" and alert on it. That produces a channel nobody reads inside a week, because "finance" appears in sentences that have nothing to do with governance, and the sentences that do matter often do not contain the word at all. What actually carries the signal is structure. A Slack message is not plain text on the wire. When someone references a channel, it arrives looking like this: Can we loop in <#C01ABCDEF|finance> before this goes out? That is a channel reference , distinct from a mention that notifies the channel, and it survives in the event payload whether or not anyone was actually alerted. It means the workspace already knows the difference between "I said the word finance" and "I pointed at the finance channel and did not bring anyone in". Nobody was reading it. So the bot parses references rather than words. It maps channel IDs to what those channels are for, and it looks for the specific shape of a message that points at a governance channel from outside it. Context beats keywords, and in this case the context was already structured and already being thrown away. Two decisions that mattered more than the detection It joins every public channel by itself. The obvious build asks an admin to add the bot wherever it should watch, which means coverage is a function of somebody remembering. Every channel created after launch is a gap, and nobody finds out until somethi

2026-09-05 原文 →
AI 资讯

Designing Type-Safe Multi-Calendar Primitives in TypeScript Without 'any'

Handling dates in JavaScript is notoriously error-prone. While ECMAScript's native Date object has well-documented pitfalls—uncontrolled mutability, 0-indexed months, and automatic local-timezone conversions—there is an even larger blind spot in existing libraries like date-fns , dayjs , and luxon : non-Gregorian calendar systems and regional legal date semantics. Global and regional enterprise applications (e.g., banking, fintech, tax compliance, healthcare, public sector, and international travel) frequently operate under official non-Gregorian legal rules: 🇹🇭 Thai Buddhist Era ( พ.ศ. = CE + 543) with official government numbering and Royal Gazette formatting presets. 🇯🇵 Japanese Imperial Era (Reiwa 令和, Heisei 平成, Showa 昭和) with exact historical day-of-event rollover boundaries (e.g., May 1, 2019 Reiwa 1 Gannen). 🇹🇼 Taiwan Minguo (民國紀年) used across municipal and legal filings. 🇸🇦 Islamic Hijri (Astronomical Umm al-Qura, Islamic Civil, and Tabular systems). 🇮🇷 Persian / Solar Hijri (Jalali Khayyami 33-year astronomical leap cycle). 🇮🇳 Indian National Saka Calendar adopted as the official civil calendar of India. To solve this without bloating runtime bundles, dragging in heavy astronomical dependencies, or resorting to loose string parsing and any , we engineered Chronera — an open-source, zero-dependency date and multi-calendar engine written in strict TypeScript. In this deep dive, we'll examine the architectural design decisions, mathematical foundations, and type-level techniques used to model complex multi-calendar domains safely. 1. The Architectural Dilemma: Monolithic Objects vs. Tagged Primitives Most date libraries wrap a native timestamp inside a single monolithic object. The instant you create a date to represent someone's birth date (e.g., 1995-05-15 ), the engine binds it to an hour, minute, second, and UTC timezone offset. When that object is serialized to JSON or transferred across servers in different timezones, classic off-by-one errors happen: //

2026-09-05 原文 →
AI 资讯

What You Refuse to Check Decides the Quality of a Linter

I built a checker for a configuration directory. The time went not into adding rules, but into deciding what not to add . Things you could detect are easy to think of. That was never the constraint. One false positive is enough to get the tool thrown out A checker is asymmetric. A miss goes unnoticed. The cost is only that you did not learn something you could have. A false positive stops the reader and demands a decision: is this actually wrong? And once someone has been burned, they read every finding with suspicion . Twice, and the tool comes out of CI. So a checker that calls a valid configuration broken is worse than no checker. Better ten rules with no false positives than thirty with one. That is obvious in the abstract and hard in practice, because while you are writing the code, every "oh, I could check that too" pulls in the other direction. No citation, no rule So I fixed one condition for adding a rule: Only check what the official documentation states outright — as an error, as skipped, or as ignored. If the documentation does not say it, the rule does not go in, however wrong the pattern looks. What this buys is that the judgement stops living in my memory. "I'm fairly sure that form was invalid" is not a citation, and my memory goes stale the moment the tool it describes releases a new version. In the implementation, every finding carries its reason: export interface Finding { severity : " error " | " warn " ; file : string ; line ?: number ; /** what is wrong, in one sentence */ message : string ; /** why that can be claimed — includes the source URL */ because : string ; } Making because required is the point. A rule you cannot justify cannot be written , because the type will not let you leave the field out. If no source comes to mind, the rule never gets implemented. The tests enforce it too: for ( const f of findings ) { if ( ! f . because . includes ( " https:// " )) fail ( `no source: ${ f . message } ` ); } One finding without a source URL fai

2026-09-05 原文 →
AI 资讯

Architecting Enterprise Angular with Signals: Zoneless Reactivity and 60fps Performance

Architecting Enterprise Angular with Signals: Zoneless Reactivity and 60fps Performance For nearly a decade, Angular relied on Zone.js to intercept asynchronous browser events and trigger top-down dirty checking across the entire component tree. In large enterprise dashboards displaying live telemetry, grid streams, and complex forms, this model leads directly to frame drops and memory leaks. With Angular 19+, fine-grained Signals provide a reactive paradigm where the framework tracks exact DOM dependencies at compile-time and updates only the precise DOM nodes that changed, unlocking 60fps zoneless execution . Architecture & Interview Cheat Sheet Feature Legacy RxJS / Zone.js Angular Signals (Modern) Change Detection Dirty-checks entire component tree Fine-grained single DOM node updates Memory Lifecycle Manual takeUntilDestroyed subscriptions Automatic graph cleanup without memory leaks Derivations Complex combineLatest / switchMap Lazy, memoized computed(() => ...) Zone.js Overhead Monkey-patches all browser async APIs 0 overhead ( provideExperimentalZonelessChangeDetection() ) 1: Clean Reactive State with Signals import { Component , computed , signal , effect , inject } from ' @angular/core ' ; export interface TelemetryPacket { id : string ; latencyMs : number ; status : ' healthy ' | ' degraded ' | ' critical ' ; } @ Component ({ selector : ' app-telemetry-monitor ' , standalone : true , template : ` <div class="card"> <h3>Live Ingestion Monitor</h3> <p>Total Packets: {{ packetCount() }}</p> <p>Average Latency: {{ averageLatency().toFixed(2) }}ms</p> <span [class.badge-warn]="isDegraded()"> {{ isDegraded() ? 'DEGRADED PERFORMANCE' : 'NOMINAL' }} </span> </div> ` }) export class TelemetryMonitorComponent { // Primary Writable Signal readonly packets = signal < TelemetryPacket [] > ([]); // Derived Computed Signals (Memoized, evaluated lazily on read) readonly packetCount = computed (() => this . packets (). length ); readonly averageLatency = computed (() => {

2026-09-05 原文 →
AI 资讯

Your Gemini Answer Has Citations. Is It Actually Grounded?

Adding citations to an AI answer feels like the moment the system becomes trustworthy. The response looks researched. Source links appear beside the text. The model is no longer answering only from its training data. But a cited answer can still be wrong. A citation may support a nearby sentence rather than the claim the user cares about. A source may be authoritative while the retrieved passage is stale. File Search may query the wrong store or document version. The model may retrieve good evidence and then write a conclusion that goes beyond it. Grounding is a capability. Trust still requires an application contract. Series note: This is Part 6 of Reliable Google AI Agents in TypeScript . The Interactions API examples use its post-May-2026 steps schema and were checked against @google/genai 2.21.0. The API remains beta, so pin and retest the SDK before copying production code. Retrieval success is not answer success Gemini can ground responses with Google Search for current public information and File Search for indexed domain-specific documents. The Interactions API exposes the execution steps and inline citation annotations, giving the application more evidence than a text completion alone. A minimal Google Search interaction looks like this: import { GoogleGenAI } from " @google/genai " ; const ai = new GoogleGenAI ({}); const interaction = await ai . interactions . create ({ model : process . env . GEMINI_MODEL ?? " gemini-3.8-flash " , input : " What changed in the public policy this week? " , tools : [{ type : " google_search " }], }); The synthesized text is only one part of the result. The steps show whether search occurred and where citations attach. type Citation = { title ?: string ; url ?: string ; citedText : string ; }; const citations : Citation [] = []; for ( const step of interaction . steps ?? []) { if ( step . type !== " model_output " ) continue ; for ( const contentBlock of step . content ?? []) { if ( contentBlock . type !== " text " ) contin

2026-09-04 原文 →