To prevent cross-site scripting in a TypeScript app, never pass untrusted data to an injection sink such as innerHTML, let your framework encode output by default, sanitize any HTML you must allow, and enforce those rules in the browser with Trusted Types and a content security policy. Types alone do not stop XSS.
Why does TypeScript not protect you?
TypeScript checks shapes at compile time. A value typed as string is a string whether it came from a developer or an attacker. The compiler has no idea that one string is safe to place in a page and another is not.
That is the whole problem. XSS is about where data lands, not what type it has. OWASP describes the goal as making sure every variable is validated and then escaped or sanitized for the place it ends up, and says any variable that skips this is a potential weakness.
Which sinks should you search for?
MDN groups browser injection sinks into three kinds. HTML sinks interpret input as markup, such as innerHTML and document.write(). JavaScript sinks interpret input as code, such as eval() and the text property of a script element. JavaScript URL sinks take a script address, such as the src of a script element.
Add framework escape hatches to the same list. OWASP names React's dangerouslySetInnerHTML, Angular's bypassSecurityTrustAs* functions, Lit's unsafeHTML and Polymer's inner-h-t-m-l attribute. Each one tells the framework to stop protecting you at that spot.
A constructed example: a support tool shows a customer's display name in a banner. A developer writes banner.innerHTML = user.displayName because the name sometimes contains an ampersand and the text looked wrong. A name containing markup now runs in the agent's session. The fix is one word: banner.textContent = user.displayName. OWASP notes that textContent creates a text node without parsing HTML, so you pass the original text without pre-encoding it.
How does encoding depend on context?
Browsers parse HTML, attributes, JavaScript, CSS and URLs differently, so one escape function cannot cover them all. OWASP lists a separate encoding for each context and says to use your framework's default first.
Three details trip up TypeScript code. Event-handler attributes decode HTML character references before the JavaScript runs, so attribute encoding alone does not make an inline handler safe. Prefer addEventListener() and pass user values as data. When you build a URL, encodeURIComponent() encodes a component but does not validate a whole URL, so a user-supplied javascript: address still needs a scheme check. OWASP also notes React 19 blocks javascript: URLs in src and href, yet says that does not replace application-specific URL validation.
What do you do when you must render HTML?
Sometimes the product really does render rich text. Then sanitize with a maintained HTML sanitizer, not a regular expression. The Rails security guide shows why filters fail: removing the word script once from a crafted nested tag leaves a working script tag behind. Allow a short list of tags and attributes instead of banning a list.
MDN describes Trusted Types as the place to wire this in. You define a policy once, and the browser then requires that any string reaching an HTML, script or script URL sink has passed through it. For HTML sinks the policy typically sanitizes. For script sinks it can block the sink or allow only fixed inputs. The API supplies no policy of its own, so you write it, and a code review only has to look at the policy instead of every call site.
What to do
- Search the codebase for
innerHTML,outerHTML,insertAdjacentHTML,document.write,eval,new Function,dangerouslySetInnerHTML,bypassSecurityTrustandunsafeHTML. List each hit with its data source. - Replace HTML sinks with
textContentor framework bindings wherever the content is text. - Where HTML is genuinely needed, run it through one shared sanitizer and one Trusted Types policy.
- Add a lint rule that fails the build on new uses of the sinks above unless an owner approves them.
- Turn on Trusted Types enforcement in report-only mode first, read the violations, fix them, then enforce.
- Validate URL schemes on any user-supplied link before it reaches
hreforsrc. - Keep framework and sanitizer packages updated, since OWASP lists out-of-date components as a source of problems.
Safeguard can help with the last item. Its SCA and reachability analysis show which vulnerable front-end packages your code actually calls, and the free in-browser scan at gold.safeguard.sh/scan covers up to 100 dependencies.
Quick check
Does React stop XSS by itself?
It encodes text bindings by default, which stops most cases. It does not protect you when you use dangerouslySetInnerHTML with unsanitized HTML or put untrusted values in places like URLs without checking them.
Is a content security policy enough?
No. OWASP says no single technique solves XSS and recommends combining encoding, sanitization and other controls. A policy limits damage but does not replace fixing the sink.
Sources
- OWASP Cross Site Scripting Prevention Cheat Sheet
- MDN: Trusted Types API
- Ruby on Rails Security Guide
Last reviewed: 18 October 2026.