Lesson 1 Guide — JavaScript for C# Developers¶
This is an intro / overview lesson. It comes before the React build. You just spent the API pass writing C#; React is written in JavaScript (and TypeScript, next lesson), the language of the browser. The good news: you already know how to program — loops, methods, types, classes are all here. This lesson maps what you know in C# onto the JavaScript syntax you'll see in every React file, and spends the most time on the few things that have no C# equivalent (destructuring, spread, modules). There's no React, no API, and no reference app yet — you verify everything by observation in a tiny throwaway project's browser console.
Goal: by the end of this lesson you can read and write the JavaScript that React
code is made of: let/const, arrow functions, template literals, objects and
destructuring, the spread operator, array methods (.map()/.filter()), and
ES module import/export — each anchored to the C# feature it corresponds to.
The general pattern you're learning: most C# concepts have a direct JavaScript
twin — a List<T> is a T[], a LINQ .Select() is a .map(), a lambda x => x is an
arrow function x => x. Learn the handful of genuinely new pieces and the rest is
translation.
Where you'll run this: you'll create one small scratch project with Vite and write plain JavaScript/TypeScript in
src/main.ts, reading the output in the browser's DevTools Console. It is deliberately separate from the real React app you build in Lesson 3 — a sandbox to try the language in, nothing more. This lesson has notableserve/reference code because it precedes the build; that's intentional.Shaky on the C# vocabulary first? This lesson maps C# onto JavaScript, so it helps to be solid on the C# side. If naming a class, object, method, parameter, field, or local variable in one of your controllers still feels fuzzy, keep the Anatomy of C# Code reference (and its C# naming conventions companion) open — the same parts, and most of the same words, reappear in the JavaScript below.
1. From C# to JavaScript¶
| You know (C#) | You're learning (JavaScript) |
|---|---|
| Compiled, runs on .NET | Interpreted, runs in the browser |
| Statically typed at compile time | Dynamically typed (types added by TypeScript, next lesson) |
List<T>, LINQ, lambdas |
arrays, array methods, arrow functions |
namespace / using |
ES modules (import / export) |
Console.WriteLine(...) |
console.log(...) → shows in the browser Console |
The mental shift: JavaScript is more permissive than C#. It won't stop you at compile time — a typo becomes a runtime surprise. That looseness is exactly why TypeScript (Lesson 2) exists, and why we use it everywhere in this course.
2. The scratch project¶
Create a one-off sandbox to run snippets in. From a folder outside your API work:
npm create vite@latest js-ts-playground -- --template vanilla-ts
cd js-ts-playground
npm install
npm run dev
--template vanilla-tsgives you plain TypeScript — no framework, just a file to run code in. (It's TypeScript, so it also serves Lesson 2 — you won't make a second project.)npm run devprints a URL (usuallyhttp://localhost:5173). Open it, then open DevTools → Console (F12).- Open
src/main.ts, delete the starter counter code, and write your own. Anything youconsole.log(...)appears in that Console. Save and the page reloads automatically.
Throughout this lesson, "run it" means: paste into src/main.ts, save, read the
Console. This is the equivalent of the C# console app you used to try language features.
We'll use one small menu throughout this lesson. Put it at the top of src/main.ts:
const menu = [
{ id: 1, name: "Nachos", price: 9.99 },
{ id: 2, name: "Ribeye", price: 24.99 },
{ id: 3, name: "Mozzarella Sticks", price: 7.5 },
];
Keep menu at the top of the file; for each ▶ Try it below, replace the code
beneath it and save. That way menu stays put and you never declare the same const
twice.
3. Variables — let, const, and no more var¶
C# infers types with var; JavaScript declares variables with let (reassignable) and
const (not reassignable). Prefer const; reach for let only when the value
changes.
const price = 9.99; // like C#'s `var price = 9.99;` but can't be reassigned
let quantity = 1; // reassignable
quantity = 2; // fine
// price = 10; // error: can't reassign a const
console.log(price, quantity);
▶ Try it — paste below
menuand save, then uncomment// price = 10;. Console:9.99 2. Uncommenting the reassignment turns that line red in the editor: Cannot assign to 'price' because it is a constant.
- There's an older keyword,
var— you'll see it in old code. Don't use it; its scoping rules are surprising.let/constare block-scoped like C# locals. - Always compare with
===/!==(strict equality), never==.==does surprising type coercion (0 == ""istrue);===behaves like C#'s==. - JavaScript has the idea of truthy/falsy: in a condition,
0,"",null,undefined, andNaNact false; everything else acts true. You'll lean on this for conditional rendering later ({items.length && ...}).
4. The primitive types (mapped to C#)¶
JavaScript has far fewer number types than C# — just one:
| C# | JavaScript | Note |
|---|---|---|
string |
string |
double or single quotes; backticks for templates (§5) |
int, long, double, decimal |
number |
one numeric type for all of them |
bool |
boolean |
true / false |
null |
null and undefined |
two "nothings" — undefined = never set, null = explicitly empty |
T[] / List<T> |
array [...] |
[1, 2, 3] |
object / Dictionary |
object {...} |
{ name: "Nachos", price: 9.99 } |
const name = "Loaded Nachos"; // string
const price = 9.99; // number
const onMenu = true; // boolean
const tags = ["spicy", "shared"]; // string[]
▶ Try it — log the values and a couple of their types:
Console:Loaded Nachos 9.99 true ['spicy', 'shared'], thennumber boolean— note9.99reports asnumber, not a separate decimal type.
The two-nothings distinction (null vs undefined) is the one surprise here — a value
that was never assigned is undefined; you use null when you deliberately mean "empty."
5. Arrow functions and template literals¶
You wrote lambdas in LINQ (staff => staff.LastName). JavaScript's arrow function is
the same idea and the same => token:
const double = (n) => n * 2; // one expression: value is returned implicitly
const greet = (first, last) => { // a block body needs an explicit return
const full = first + " " + last;
return full;
};
console.log(double(5)); // 10
console.log(greet("Sam", "Diaz")); // Sam Diaz
Arrow functions are everywhere in React — every event handler and every .map()
callback is one.
Template literals are C#'s $"..." string interpolation, using backticks and
${ }:
const item = "Nachos", price = 9.99;
const label = `${item} — $${price}`; // C#: $"{item} — ${price}"
console.log(label); // Nachos — $9.99
▶ Try it — paste both snippets beneath
menu. Console:10, thenSam Diaz, thenNachos — $9.99.
6. Objects and destructuring¶
An object literal is a bag of key/value properties — the JS stand-in for a C# object or
Dictionary:
const menuItem = { id: 1, name: "Nachos", price: 9.99 };
console.log(menuItem.name); // dot access, like C#
Destructuring has no C# equivalent and is worth slowing down for — it pulls properties out into their own variables in one line:
const { name, price } = menuItem; // two consts in one statement
console.log(name, price); // Nachos 9.99
Arrays destructure by position:
▶ Try it — destructure the first item from the
Console:menuin section 2:Nachos 9.99
You'll see destructuring constantly in React — const [value, setValue] = useState(),
and pulling props out of a component's argument. The payoff lands in Lesson 5, where
you'll receive your first component prop and destructure it across three steps until it
clicks.
7. The spread operator (...)¶
... spreads the contents of one array or object into another — a copy-and-extend
in one token. No direct C# equivalent.
const base = ["spicy", "shared"];
const more = [...base, "popular"]; // ["spicy", "shared", "popular"] — a NEW array
const item = { id: 1, name: "Nachos" };
const priced = { ...item, price: 9.99 }; // { id, name, price } — a NEW object
▶ Try it — spread a menu item into a new object, then check the original is untouched:
Console:{ id: 1, name: 'Nachos', price: 9.99, onSpecial: true }, then the original{ id: 1, name: 'Nachos', price: 9.99 }— spread copied, it didn't mutate.
The key idea: spread makes a new value rather than mutating the original — the
functional style React relies on for state updates. You'll also meet spread in
react-hook-form ({...register("name")} spreads several props onto an input) and in
route setup. For now just recognize the token and that it copies.
8. The evolution of loops — from for to .map()¶
This is the single most important pattern to bring into React, so watch how it evolves. Say you have numbers and want each one times ten.
Stage 1 — the for loop. The C-style loop you know from C#: manage an index, push
into a new array.
const numbers = [1, 2, 3];
const tens = [];
for (let i = 0; i < numbers.length; i++) {
tens.push(numbers[i] * 10);
}
Stage 2 — forEach. A method that calls a function once per element — no index to
manage, but you still push manually.
Stage 3 — .map(). map transforms an array into a new one: give it a function,
get back a new array of the results. No temp array, no push.
▶ Try it — run the three stages one at a time (each one replaces the last beneath
Console:menu). Each logs the same thing:[10, 20, 30]
.map() is exactly LINQ's .Select() (numbers.Select(n => n * 10) in C#), and
.filter() is LINQ's .Where() — run it on the menu from section 2:
▶ Try it — paste and save.
Console: two items —Nachos($9.99) andMozzarella Sticks($7.5);Ribeyeis filtered out.
Both .map() and .filter() return a new array and don't touch the original — same
functional spirit as LINQ.
Why does this matter for React? Because rendering a list is a transform: an array of
data mapped into an array of UI. In Lesson 3 you'll write exactly this — and you'll walk
the same for → forEach → map progression again, but transforming data into JSX
elements instead of numbers. .map() is how React renders every list.
9. ES Modules — import and export¶
C# organizes code with namespace and using. JavaScript uses modules: each file
exports what it wants to share, and other files import it. Two flavors:
Named exports — export several things by name; import them in { } braces:
// money.ts
export const TAX_RATE = 0.07;
export function format(n: number) { return `$${n.toFixed(2)}`; }
// main.ts
import { TAX_RATE, format } from "./money";
Default export — one "main" thing per file; import it with any name, no braces:
// MenuItemsPage.tsx
export default function MenuItemsPage() { /* ... */ }
// App.tsx
import MenuItemsPage from "./menuItems/MenuItemsPage";
You'll use both against real libraries this course installs — the pattern is the same:
import App from "./App"; // default: the app's root component
import { BrowserRouter } from "react-router-dom"; // named: one of many router exports
import { useForm } from "react-hook-form"; // named: the form hook
Rule of thumb we follow: components are default exports (one per file), while
helpers, interfaces, and hooks are named exports. When you see { } in an import,
it's a named export; no braces means default. This is how every file in the Lesson 3
project will connect to the others.
▶ Try it — make
src/money.tswith the namedformatexport shown above. Add the import at the top ofmain.ts, then log belowmenu:Console:import { format } from "./money"; // ...menu and the rest below... console.log(format(menu[0].price));$9.99— pulled from another file and wired by a named export.
10. Verifying in the browser¶
There's no API and no reference app — you verify by reading the Console. With
npm run dev running and src/main.ts open:
console.log(...)a value and confirm it prints in DevTools → Console (F12).- Try to reassign a
const— the editor/Console shows an error. Change it toletand it works. That's the difference between the two. .map()an array and log the result — confirm you get a new array and the original is unchanged (console.log(original)after).- Split a helper into its own file,
exportfrom it,importintomain.ts, and log the result — proof that modules connect files.
If a change doesn't show, check the Console for an error and confirm the page reloaded.
The General Pattern (what to take away)¶
constby default,letwhen it changes; nevervar; compare with===.- One
numbertype;string,boolean; two nothings (null/undefined). - Arrow functions (
x => ...) are lambdas; template literals (`${x}`) are$"...". - Destructuring (
const { a, b } = obj) and spread ({...obj}) are the two new ideas with no C# twin — spread makes new values instead of mutating. .map()= LINQ.Select(),.filter()= LINQ.Where(); both return new arrays. This is how React renders lists.- Modules: default export for components, named exports (
{ }) for helpers/hooks.
Everything in this pass is built from these. Lesson 2 adds types back on top; Lesson
3 renders your first real components with .map().
Build Steps¶
- Scaffold the scratch project:
npm create vite@latest js-ts-playground -- --template vanilla-ts, thencdin,npm install,npm run dev. - Open the printed URL and its DevTools → Console; clear
src/main.ts. - Declare values with
const/let; log them; try reassigning aconstto see the error. - Write an arrow function and a template-literal string; log both.
- Build an object; destructure two properties out of it; spread it into a new object with an added property.
- Take an array and transform it three ways —
forloop,forEach, then.map()— and.filter()it; confirm each result in the Console. - Move a helper into its own file,
exportit,importit intomain.ts, and log the result.