Learning React and TypeScript with AI without giving up control
I remember what learning a new technology looked like years ago: hours of YouTube videos, tutorials, Stack Overflow threads, and endless Google searches. Whenever I did not understand something, I often wished I could simply ask someone a question. The process was difficult and frequently frustrating, but that frustration eventually turned into knowledge. Even when it was painful, it was still my process. I made the mistakes, found the answers, and slowly began to understand how everything worked.
Today, beginners can ask AI to generate code, create entire applications, and solve problems they are not yet sure how to approach. The important question is no longer only whether they can build something. It is whether they can use AI and still remain in control of the learning process.
Recently, I started a small project. The idea is an AI code review platform, but the real goal is slightly different: I want to become more confident with React and TypeScript. I already work with code every day, but frontend development is still an area where I want to improve. I do not want to copy working components and hope that understanding will come later. I want to know why data has a particular shape, why state belongs in one place rather than another, and how TypeScript can help me instead of merely complaining in red text. So I decided to build something intentionally limited: a React and TypeScript dashboard displaying mocked code review runs. No Rails API yet. No GitHub integration yet. No real AI integration yet. Just the frontend, mock data, and learning.
Starting with rules, not code
The first thing I created was not a component. It was a set of rules describing how I wanted to use AI during the project. This matters to me because AI can make learning smoother, but it can also make it dangerously easy to skip the part where understanding happens.
I created a simple learning contract:
- I write the code first.
- AI can explain concepts.
- AI can help me debug.
- AI can review my approach.
- AI can suggest alternatives and trade-offs.
- AI should not write the complete implementation unless I explicitly ask for it.
It may sound unnecessarily formal, but boundaries are useful when a tool can produce an entire solution in seconds. Without them, it is easy to stop learning and start watching AI work. For this project, I want a different process: more discussion and less outsourcing. I really want to learn React, not merely end up with a React application.
Making the project intentionally boring
The application could become complicated very quickly. An AI code review platform sounds as though it should include GitHub integration, authentication, background jobs, an API, pull request diffs, and many other features. When I started discussing the idea with AI, the plan expanded almost immediately. That is how another unfinished side project is born: extremely productive during the planning phase, then abandoned somewhere between authentication and the third database migration. I had to remind myself of the actual goal: learning React.
For the first phase, I limited the application to a mocked dashboard containing:
- review runs
- pull requests
- repositories
- review findings
- statuses
- severities
- categories
It is not the most exciting version of the product, but it gives me an opportunity to practise exactly what I want to learn:
- React components
- TypeScript domain modelling
- props
- state
- derived data
- filtering
- project structure
A small, working foundation can teach me more than an ambitious architecture I never finish. I still want to experience some of the frustration. I want to read the documentation, test ideas, and make mistakes. The difference is that now, when I get stuck, I can ask AI a focused question.
The first real task: types and mock data
My first implementation task was defining the domain types. I created types for entities such as:
ReviewRunReviewFindingRepositoryPullRequest
I also defined possible review statuses, including queued, running, completed, and failed, as well as finding severities such as low, medium, high, and critical. This looked straightforward at first, but it quickly raised questions.
Should findings be nested inside a review run, or stored separately and connected using reviewRunId? Should lineNumber always be present, or can a finding refer to an entire file? What should happen when a review has no findings? Can a finding be dismissed?
These may seem like small decisions, but they influence how the UI will work later.
This was one of the places where AI became genuinely useful. I did not ask it to define the types for me. Instead, I used it like a developer sitting next to me and questioning my assumptions:
Are you sure this data shape will still work when you add filters? What happens when a review has no findings? How will you represent a dismissed finding?
Those questions made me consider states I might otherwise have ignored while building only the happy path.
Using AI for discussion, not implementation
The most valuable part of the process was not code generation. It was being able to discuss the model and compare different options. For example, I decided to store review runs and findings in separate arrays connected by IDs. This makes rendering slightly less convenient because I need to select findings using reviewRunId. However, it is also closer to the kind of data structure I may receive from an API later. AI helped me identify the trade-off, but it did not make the decision for me.
That distinction matters. If I simply accept a generated solution, I may be unable to explain it later. When I compare alternatives and choose one deliberately, the decision becomes part of my understanding.
This is how I currently find AI most useful: not as something that removes every problem, but as something that helps me examine a problem from more angles.
How juniors can use AI to learn
AI can be very useful for junior developers, but perhaps not in the most obvious way.
The obvious prompt is:
Write this for me.
A more useful one is:
Help me understand what I am doing.
Instead of asking AI to build an entire component, you can ask:
- What concept am I missing here?
- What questions should I answer before I start coding?
- What states should this interface support?
- Which edge cases am I likely to forget?
- Can you review my type model and identify weak assumptions?
- Can you give me a debugging checklist without fixing the problem?
- Can you show me a small, isolated example instead of code from my project?
This creates a very different relationship with the tool.
AI stops being only a code generator. It becomes a mentor, rubber duck, reviewer, and an unusually patient companion for searching through documentation. It will not always be correct. It can misunderstand the problem, make poor assumptions, or confidently suggest something unnecessary. The developer still needs to evaluate the answer. That evaluation is also part of learning.
Keeping ownership of decisions
For me, staying in control means keeping ownership of the decisions. AI can suggest, explain, compare, and warn. I still need to choose what belongs in the project and understand why. This is especially important for beginners because generated code can look professional even when the person using it does not understand it. Professional-looking code and understood code are not the same thing. These are some of the rules I try to follow:
- Write my own version first, even when it is ugly.
- Ask for hints before asking for an implementation.
- Request trade-offs instead of a single answer.
- Keep the scope small enough to understand every part.
- Reject code I cannot explain.
- Continue asking why until the answer makes sense.
- Ask AI to review my reasoning rather than replace it.
This approach is slower than generating everything. But learning is not measured only by how quickly something appears on the screen. At the end of the project, I do not want to say:
AI built this.
I want to be able to say:
I built this, and AI helped me think.
That difference matters.
Learning should still include struggle
Most tools are designed to remove friction. During learning, however, some friction is useful. If I never struggle with props, state, types, component boundaries, or data modelling, I will not fully understand them. AI can remove so much difficulty that I feel productive while quietly skipping the uncomfortable part where knowledge is formed. My goal is not to avoid frustration completely. It is to use AI when the frustration stops being useful.
There is a difference between being stuck because I need to think more carefully and being stuck because I am missing one concept that someone could explain in two minutes. AI is particularly helpful in the second situation. The challenge is learning to recognise which situation I am in.
Next step
My next task is to build the first real interface: a list of review runs.
This should give me an opportunity to practise:
- passing typed data into components
- rendering lists
- displaying status badges
- calculating finding counts
- deciding which information belongs in the table
This is probably the point where React will begin to feel more real than it did during the setup phase. For now, I have a project brief, a set of learning rules, domain types, and mock data. There is not much on the screen yet, but there is enough of a foundation to continue. More importantly, I can explain why the project is structured this way. That is the part I care about most. So yes - I want junior developers to use AI. I just hope they use it in a way that gives them more understanding, not less.